Манипуляторы потоков
От: Аноним  
Дата: 04.10.05 16:56
Оценка:
Проблема заключается в том что надо написать довольно много манипуляторов с аргументами, как можно это сделать с меньшими усилиями?

Самый короткий способ который мне удолось придумать выглядит примерно так:

template<class T1, class T2, class T3, class T4>
struct manip4_type {
  typedef boost::_bi::bind_t<StreamType&, StreamType&(*)(StreamType&, T1, T2, T3, T4),
          boost::_bi::list5<boost::arg<1>,
            boost::_bi::value<T1>,
            boost::_bi::value<T2>,
            boost::_bi::value<T3>,
            boost::_bi::value<T4>
          > >
          type;
};

inline StreamType& new_manip_func(StreamType &stream, T1 t1, T2 t2, T3 t3) {
  stream.new_manip(t1, t2, t3, t4); return stream;
}
inline manip4_type<T1, T2, T3, T4>::type new_manip(T1 t1, T2 t2, T3 t3) {
  return boost::bind(new_manip_func, _1, t1, t2, t3, t4);
}


manip[1,2,3,4,5,6]_type нужен для определения возвращаемого типа, другого способа опредилить тип bind'а я придумать не смог.

Заранее спасибо.
Re: Манипуляторы потоков
От: Centaur Россия  
Дата: 04.10.05 19:21
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Проблема заключается в том что надо написать довольно много манипуляторов с аргументами, как можно это сделать с меньшими усилиями?


class MyManipulatorWith4Arguments
{
public:
  MyManipulatorWith4Arguments(T1 a1, T2 a2, T3 a3, T4 a4)
  : a1_(a1), a2_(a2), a3_(a3), a4_(a4)
  {}

  friend std::ostream& operator<<(std::ostream& stream, const MyManipulatorWith4Arguments& manip)
  {
    // do something useful with stream and manip.a1_–a4_
  }
};

Вовсе не вижу причин обязательно реализовывать манипулятор через вывод в поток функции от потока…
Re: Манипуляторы потоков
От: Кодт Россия  
Дата: 04.10.05 22:37
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Проблема заключается в том что надо написать довольно много манипуляторов с аргументами, как можно это сделать с меньшими усилиями?


Можно писать объекты-манипуляторы, унаследованные от базового
class a_manip
{
  virtual void operator()(std::ostream& ost) const {}
  virtual void operator()(std::istream& ist) const {}
public:
  friend std::ostream& std::operator<<(std::ostream& ost, const a_manip& m) { m(ost); return ost; }
  friend std::istream& std::operator>>(std::istream& ist, const a_manip& m) { m(ist); return ist; }
};

Причём порождать классы манипуляторов с помощью макросов.

А можно сделать вот так:
template<class F> // F - унарный функтор от потока
class func_manip_t // специальная обёртка, чтобы обозначить, что этот функтор - манипулятор
{
  F f_;
public:
  manip_t(F f) : f_(f) {}

  friend std::ostream& std::operator<<(std::ostream& ost, const a_manip& m) { f_(ost); return ost; }
  friend std::istream& std::operator>>(std::istream& ist, const a_manip& m) { f_(ist); return ist; }
};
template<class F>
func_manip_t<F> fmanip(F f) { return func_manip_t<F>(f); }

.....
void doit(std::ostream&, int t1=0, int t2=0);
void hello(const char*, std::ostream&);
void byebye(std::ostream&, const char*);
void jabberwacky(int x, int y, int z, int t, std::ostream&);
.....
// если нельзя просто записать  cout << doit, всегда можно завернуть её в fmanip
std::cout << fmanip(doit);
// связывать можно любым подходящим способом
std::cout << fmanip(boost::bind(hello,"world"));
std::cout << fmanip(std::bind1st(std::ptr_fun(hello),"world"));
// если поток - не последний аргумент, то связываем соответственно
std::cout << fmanip(std::bind2nd(std::ptr_fun(byebye),"baby"));
// если много аргументов - то boost::bind или boost::lambda нас спасут
std::cout << fmanip(boost::bind(jabberwacky,1,2,3,4));
// наконец, можно связать заранее
boost::function<void(std::ostream&)> f = boost::bind(jabberwacky,1,2,3,4);
std::cout << fmanip(f);
.....
Перекуём баги на фичи!
Re[2]: Манипуляторы потоков
От: Аноним  
Дата: 05.10.05 04:31
Оценка:
C>Вовсе не вижу причин обязательно реализовывать манипулятор через вывод в поток функции от потока…

Смысл в том что бы не трогать класс потока. Вовторых, черезмерная перегрузка оператора вывода может вызвать ряд проблем.

К>.....
К>void doit(std::ostream&, int t1=0, int t2=0);
К>void hello(const char*, std::ostream&);
К>void byebye(std::ostream&, const char*);
К>void jabberwacky(int x, int y, int z, int t, std::ostream&);
К>.....
К>// если нельзя просто записать  cout << doit, всегда можно завернуть её в fmanip
К>std::cout << fmanip(doit);
К>// связывать можно любым подходящим способом
К>std::cout << fmanip(boost::bind(hello,"world"));
К>std::cout << fmanip(std::bind1st(std::ptr_fun(hello),"world"));
К>// если поток - не последний аргумент, то связываем соответственно
К>std::cout << fmanip(std::bind2nd(std::ptr_fun(byebye),"baby"));
К>// если много аргументов - то boost::bind или boost::lambda нас спасут
К>std::cout << fmanip(boost::bind(jabberwacky,1,2,3,4));
К>// наконец, можно связать заранее
К>boost::function<void(std::ostream&)> f = boost::bind(jabberwacky,1,2,3,4);
К>std::cout << fmanip(f);
К>.....
К>


Спасибо, довольно интересно, но не подходит, виртуальный базовый класс не подходит потому что все эти функции дожны быть встроены. Вариант с fmanip тоже не катит, поскольку в результате это должно выглядеть несколько иначе:

stream << do_this(t1, t2, t3)

       << and_this(t1, t2)
;


Поповоду макросов, как передать в макросу аргумент с запятыми?
Re[3]: Манипуляторы потоков
От: Centaur Россия  
Дата: 05.10.05 05:05
Оценка:
Здравствуйте, Аноним, Вы писали:

C>>Вовсе не вижу причин обязательно реализовывать манипулятор через вывод в поток функции от потока…


А>Смысл в том, чтобы не трогать класс потока.


Так а он и не трогается…

А>Во-вторых, чрезмерная перегрузка оператора вывода может вызвать ряд проблем.


А поподробнее о проблемах? Проблемы будут, если забыть сделать конструктор манипулятора явным (explicit).

Кстати, подход почерпнут из Conversations #10: Manipulations, by Jim Hyslop and Herb Sutter, C++ Users Journal, April 2001.
Re[3]: Манипуляторы потоков
От: Elich  
Дата: 05.10.05 07:28
Оценка:
А>Поповоду макросов, как передать в макросу аргумент с запятыми?

#define COMMA ,
#define TEST(a, c) a = c

int main()
{
  int a = 10, b = 20, c = 30;
  TEST(a COMMA b, c);
  
  std::cout << a << std::endl;
  std::cout << b << std::endl;
  std::cout << c << std::endl;
  return 0;
}


Output:

10
30
30

Re[4]: Манипуляторы потоков
От: Глеб Алексеев  
Дата: 05.10.05 07:46
Оценка: 1 (1)
Здравствуйте, Elich, Вы писали:

А>>Поповоду макросов, как передать в макросу аргумент с запятыми?


E>[code]

E>#define COMMA ,
E>#define TEST(a, c) a = c

ИМХО, проще добавить лишнюю пару скобок.
#define MACRO(x) x
int main() {
  int a = MACRO(5);
    int b = MACRO((std::min(7,8)));
};
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[3]: Манипуляторы потоков
От: Кодт Россия  
Дата: 05.10.05 09:06
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Спасибо, довольно интересно, но не подходит, виртуальный базовый класс не подходит потому что все эти функции дожны быть встроены. Вариант с fmanip тоже не катит, поскольку в результате это должно выглядеть несколько иначе:

А>stream << do_this(t1, t2, t3)

А>       << and_this(t1, t2)
А>;


Ты уверен, что инлайновость критична? Это результат профилирования, горького опыта или предубеждение?
Учти, что boost::bind тоже вызывает функцию косвенно.
Перекуём баги на фичи!
Re[5]: Манипуляторы потоков
От: Elich  
Дата: 05.10.05 13:35
Оценка: 1 (1)
E>>[code]
E>>#define COMMA ,
E>>#define TEST(a, c) a = c

ГА>ИМХО, проще добавить лишнюю пару скобок.


Честно говоря, не знал, что в списке параметров макроса можно использовать скобки.

Согласен, в приведенном мной примере действительно проще и ощутимо нагляднее.
То же самое и в других случаях, когда сами скобки будут корректны в выражении после подстановки.

Дополнительный макрос для запятой может помочь в тех ситуациях, когда скобки в выражении после подстановки не будут допустимыми.
Например:
#define MACRO(x) x
MACRO((std::vector<std::string, std::allocator<std::string> >)) var; // ошибка
Re[4]: Манипуляторы потоков
От: Аноним  
Дата: 05.10.05 14:18
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, Аноним, Вы писали:


А>>Спасибо, довольно интересно, но не подходит, виртуальный базовый класс не подходит потому что все эти функции дожны быть встроены. Вариант с fmanip тоже не катит, поскольку в результате это должно выглядеть несколько иначе:

К>
А>>stream << do_this(t1, t2, t3)

А>>       << and_this(t1, t2)
А>>;
К>


К>Ты уверен, что инлайновость критична? Это результат профилирования, горького опыта или предубеждение?

К>Учти, что boost::bind тоже вызывает функцию косвенно.

Да, уверен, что является результатом предубеждения.

Всем спасибо, все понял, все сделал. Очень признателен.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.