Захотелось что-то вроде boost::function, но чтобы оно хранило одновременно несколько функциональных объектов, и выбор осуществлялся в заисимости от аргументов — для иммитации перегрузки или параметров по умолчанию. Предполагается хранение до, допустим, шести объектов, в основном boost::bind.
Кто-нибудь думал о/сталкивался с/реализовывал подобное ? Какие будут идеи ?
Здравствуйте, Alexander G, Вы писали:
AG>Какие будут идеи ?
Естественно, написать аггрегат.
То есть:
class my_fancy_function
:private std::function<[call-specification-1]>
,private std::function<[call-specification-2]>
....
,private std::function<[call-specification-N]>
{
// using std::function<[call-specification-1]>::operator();
...
using std::function<[call-specification-N]>::operator();
// единственный конструктор
my_fancy_function() {}
// инициализаторы - для всех вариантов вызоваtemplate<class T> void init_specifiction_1(T arg)
{ static_cast< std::function<[call-specification-1]>& >(*this) = arg; }
};
//и profit!
Но, есть одна тонкость, если начать пытаться писать конструкторы, то получится черт-те что. В аггрегате могут начать инициализироваться не те подклассы.
Посему, не стоит делать инициализирующие конструкторы, которые будут "сами" выбирать вариант инициализации. Лучше сделать набор инициализирующих функций, которые и надо вызывать для этого.
Здравствуйте, Alexander G, Вы писали:
AG>Захотелось что-то вроде boost::function, но чтобы оно хранило одновременно несколько функциональных объектов, и выбор осуществлялся в заисимости от аргументов — для иммитации перегрузки или параметров по умолчанию. Предполагается хранение до, допустим, шести объектов, в основном boost::bind.
AG>Кто-нибудь думал о/сталкивался с/реализовывал подобное ? Какие будут идеи ?
Здравствуйте, Alexander G, Вы писали:
AG>Захотелось что-то вроде boost::function, но чтобы оно хранило одновременно несколько функциональных объектов, и выбор осуществлялся в заисимости от аргументов — для иммитации перегрузки или параметров по умолчанию. Предполагается хранение до, допустим, шести объектов, в основном boost::bind.
AG>Кто-нибудь думал о/сталкивался с/реализовывал подобное ? Какие будут идеи ?
(шепотом) boooooosssssst
конкретно, Boost.Overload.
Она еще не включена, так что бери из vault, доки здесь: http://docs.google.com/Doc?id=dhd386mp_16gxsfxn&invite=gdr7gtq (не могу обещать, что линк работает — у нас на работе гугль забанен), так что, на всякий случай, вот примеры использования, оно?
(ниже идет порядка 200 строк кода)
/*
Author: Marco Costalba (C) 2007-2008
To compile:
(gcc) g++ -O2 -o test test.cpp
(MSVC) cl /Ox /EHs test.cpp
An overload it's a multi-signature function wrapper.
Once defined on a set of signatures it is possible to assign with
overload::set<>() any function, function object or even pointer
to member function to the overload as long as the signature
of the assigned function/functor matches one of the overload's
signatures set.
It is possible to assign many functions/functors as long as each
one has a different signature, otherwise the latest assignment
overwrites the earlier with the same signature.
Overload uses copy semantic for functors, so that a copy of the
assigned functor is stored. This implies that functors must be copy
constructible.
An overload supports value semantic itself being copy constructible
and assignable.
When overload is invoked the calling arguments are forwarded to the
correct assigned function, an overload_ns::bad_function_call exception
will be thrown if the corresponding signature slot is empty.
User can call overload::is_set<Sig>() to check if slot corresponding
to signature Sig is empty.
Finally user can clear a slot corresponding to signature Sig with
overload::reset<Sig>()
Internally, given a set of signatures, overload instantiates a hierarchy
of classes, one per signature, and each class defines an operator()
that interface a given signature and forwards it's arguments to the
assigned function/functor.
When an overload is called compiler will link the correct operator()
according to the argument's type and arity.
Overload signatures rules should follow strictly what states C++ for
overloading of operator() member functions, so as example if an overload
is called with an argument for which no exact match occurs, but that can
be converted by more then one overloaded operator() then a compile error
occurs of type 'ambiguous call'.
Anyhow implicit conversions at invocation time are supported as long as
there is no ambigity on the function to call.
Overload code is self contained, no external libraries are needed apart
from the standard one.
Because overloads support value semantic it is possible to store them in
any kind of container to obtain a very powerful and flexible dispatcher.
Following examples will clarify overload usage.
*/#include"overload.hpp"#include <cassert>
#include <iostream>
#include <string>
#include <map>
using std::string;
int foo1(char) { return -1; }
double foo2(int, char) { return 123.456; }
char foo3(string) { return'x'; }
void foo4(string s1, string s2, string s3) { std::cout << s1 + s2 + s3; }
int foo5() { return 7; }
template<typename T>
T fooT(T const& t) { return -t; }
struct foo1_t
{
int operator()(char) { return 123; }
int operator()(char) const { return 123; }
char foo3(string) { return'y'; }
int foo1(char) { return 8; }
static char foo3_s(string) { return's'; }
};
int main(int, char**)
{
overload_ns::overload
< int(char)
, double(int, char)
, int()
, double(double const&)
, void(string, string, string)
, char(string)
> f(foo4, &foo1, fooT<double>); /* Assign functions directly in the c'tor */
/* Use is_set() to check if a given signature slot is empty */
assert( f.is_set<int(char)>() == true );
assert( f.is_set<int()>() == false );
typedef int (Fun)();
Fun* foo5_ptr(foo5);
Fun& foo5_ref(foo5);
/* Assign functions in any order as pointers, references or plain types */
f.set(&foo2);
f.set(foo5_ptr);
f.set(foo5_ref);
f.set(foo3);
assert( f('x') == -1 );
/* Assign functors as well as pointers, references or plain types */
foo1_t functor;
foo1_t& functor_ref(functor);
f.set(&functor); // overwrites foo1()
f.set( functor);
f.set( functor_ref);
/* Works also with const objects */const foo1_t const_functor(functor);
f.set(&const_functor);
f.set( const_functor);
/* Implicit conversions are supported at invocation time */
f("\nHello", " World", " I'm Marco\n");
assert( f('x') == 123 );
assert( f(123, 'x') > 123.455 && f(123, 'x') < 123.457 );
assert( f("hello") == 'x' );
assert( f() == 7 );
assert( f(-3.0) > 2.9 && f(-3.0) < 3.1 );
/* Try with static member function */
f.set(foo1_t::foo3_s);
assert( f("hello") == 's' );
/* Assign a pointer to member function */
f.set(&functor, &foo1_t::foo3); // overwrites foo3()
f.set( functor, &foo1_t::foo1); // overwrites functor.operator()
assert( f("hello") == 'y' );
assert( f('x') == 8 );
/* Finally, it is possible to reset a given signature */
assert( f.is_set<int(char)>() == true );
f.reset<int(char)>();
assert( f.is_set<int(char)>() == false );
/* An exception is thrown if an empty slot is called */try {
f('x');
assert(false); // fail if we arrive here
}
catch (overload_ns::bad_function_call& e)
{
std::cout << std::endl << e.what() << std::endl;
}
/* An overload is copy constructible and assignable */typedef overload_ns::overload< int(char)
, double(int, char)
, int()
, double(double const&)
, void(string, string, string)
, char(string)
> Overload;
Overload h(f), g;
assert( h(123, 'x') > 123.455 && h(123, 'x') < 123.457 );
assert( h("hello") == 'y' );
g = h;
assert( g(123, 'x') > 123.455 && g(123, 'x') < 123.457 );
assert( g("hello") == 'y' );
/* Because of value semantic we can store them in an array */
Overload d[5];
d[0].set(foo1);
d[1].set(functor, &foo1_t::foo1);
assert( d[0]('x') == -1 );
assert( d[1]('x') == 8 );
/* Any container is suitable, as example a std::map, so to
* easily obtain a powerful runtime dynamic dispatcher with
* multi-signature (heterogeneous function types) support.
*/
std::map<string, Overload> m;
m["First"] = g;
m["Second"] = Overload(foo3, foo1);
m["First"].set(functor, &foo1_t::foo1);
assert( m["First"]('x') == 8 );
assert( m["Second"]('x') == -1 );
assert( m["First"]("hello") == 'y' );
assert( m["Second"]("hello") == 'x' );
std::cout << "\nTest successful\n" << std::endl;
return 0;
}
Здравствуйте, Юрий Жмеренецкий, Вы писали:
ЮЖ>Здравствуйте, jazzer, Вы писали:
J>>конкретно, Boost.Overload
ЮЖ>Вот еще из boost::vault: Multi signature boost::function extension
Похоже что это одно и тоже, только overload_ns::overload заменен на mpl::vector...
Здравствуйте, jazzer, Вы писали:
J>вот примеры использования, оно?
оно
интерфейс соответствует всем моим пожеланиям:
1. Type erasure. Т.е. возможность затайпдефить typedef boost::overload<int (void), void(int)> my_callback; (нужно было именно это, а не выводимый тип, вроде boost::bind)
2. Метод set с шаблонным параметром сигнатурой. Выводит шаблонный параметр из boost::function, boost::bind и других функторов.
3. Конструктор, в котором можно перечислять функции в произвольном порядке.
J>доки здесь: http://docs.google.com/Doc?id=dhd386mp_16gxsfxn&invite=gdr7gtq
Работает, надо только залогинится в гугл.
Сама либа здесь.
Это не то что я хочу.
Нужен не вроде boost::bind, т.е. выводимый тип, а нечто, что можно затайпдефить и использовать в качестве формального параметра виртуальной функции. То что называют "type erasure". Т.е. test д.б. не-шаблонной.
Временные boost::function не очень удобно, хотелось передавать boost::bind напрямую.
Лучше обычный метод, а не оператор, чтобы можно было задать сигнатуру явно.
ЮЖ>Реализация правда, восторга не вызывает. Постить пока не буду...
Свою я тоже не запосчу. Уже посмотрел на Boost.Overload
Это надо несколько раз упоминать каждую при объявлении типа ? Или предлагается использовать какую-либо хотрую препроцессорную магию ? не, не то.
И С++0х для меня пока ещё светлое будущее.
DEA>// единственный конструктор
DEA> my_fancy_function() {}
про конструктор ниже.
DEA>// инициализаторы - для всех вариантов вызова
DEA> template<class T> void init_specifiction_1(T arg)
DEA> { static_cast< std::function<[call-specification-1]>& >(*this) = arg; }
DEA>};
а почему бы не выводить сигнатуру из шаблонного параметра, зачем мне эти _1 ?
DEA>//и profit!
Слишком много ??????????? до профита.
DEA>Но, есть одна тонкость, если начать пытаться писать конструкторы, то получится черт-те что. В аггрегате могут начать инициализироваться не те подклассы. DEA>Посему, не стоит делать инициализирующие конструкторы, которые будут "сами" выбирать вариант инициализации. Лучше сделать набор инициализирующих функций, которые и надо вызывать для этого.
А я не вижу, почему выбор вариантов инициализации может быть ошибочен. Можно однозначно вывести сигнатуру, и такая сигнатура есть — инициализируем, явно дали сигнатуру — тоже инициализируем, иначе — фэйл на этапе компиляции. вот у Boost.Overload это вроде удалось.
Здравствуйте, Юрий Жмеренецкий, Вы писали:
ЮЖ>Здравствуйте, jazzer, Вы писали:
J>>(шепотом) boooooosssssst
ЮЖ>)
ЮЖ>Вот еще из boost::vault: Multi signature boost::function extension
А ведь судя по датам MSF это более новая Boost.Overload, таки наверное её и заюзаем.
Здравствуйте, Alexander G, Вы писали:
AG>А ведь судя по датам MSF это более новая Boost.Overload, таки наверное её и заюзаем.
даты тут плохой советчик.
лучше мейл-лист смотреть на предмет обсуждения (я не смотрел, не до того), там обычно, прежде чем предложить библиотеку в буст, забрасываются удочки на предмет изучения интереса сообщества.
Здравствуйте, jazzer, Вы писали:
J>лучше мейл-лист смотреть на предмет обсуждения (я не смотрел, не до того), там обычно, прежде чем предложить библиотеку в буст, забрасываются удочки на предмет изучения интереса сообщества.
Из недавних упоминаний — только вот.
MSF действительно более новая.