From: Ben on 3 Nov 2009 06:28 Hi I need to associate a string with a function. I define the following table struct COMMAND { const WCHAR * name; void T::*proc(int); }; static const COMMAND commands[] = { {"north", fun0}, {"east", fun1}, {"south", fun2}, {"west", fun3}, }; However I would like to have fun2 and fun3 corresponds to another type of function T::*proc(string) How can I do it? Thanks a lot! -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Ulrich Eckhardt on 3 Nov 2009 18:19 Ben wrote: > Hi I need to associate a string with a function. I define the > following table > > struct COMMAND { > const WCHAR * name; > void T::*proc(int); > }; Three things: 1. Reserve ALL_UPPERCASE for macros. 2. WCHAR is not a C++ type, it's a typedef from the win32 API. 3. The general approach would be to use a std::map... > static const COMMAND commands[] = { > {"north", fun0}, > {"east", fun1}, > {"south", fun2}, > {"west", fun3}, > }; .... but you can't create a static const map like this. BTW: In C++, all constants have internal linkage, so you can just drop the static attribute here without namespace pollution or ODR violations. Lastly, you want L"wide" literals. > However I would like to have fun2 and fun3 corresponds to another type > of function > T::*proc(string) > > How can I do it? You can't, as a function pointer can only be of one type. The typical solution is to pass as argument that can be either an int or a string, like e.g. the input stream you are reading from. Alternatively, you could pass a void pointer and something indicating what it points to. Depending on what you want to do with this, you might also want to take a look at Boost.Bind. Lastly, you could create an array with different types, aka structure: struct command_table { void (T::*north)(int); void (T::*south)(string); }; ....or maybe even use virtual functions: class unit { virtual void north(int); virtual void south(string); }; Uli -- Sator Laser GmbH Geschäftsführer: Thorsten Föcking, Amtsgericht Hamburg HR B62 932 [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Jeff Schwab on 3 Nov 2009 18:18 Ben wrote: > Hi I need to associate a string with a function. I define the > following table > > struct COMMAND { > const WCHAR * name; > void T::*proc(int); > }; > > static const COMMAND commands[] = { > {"north", fun0}, > {"east", fun1}, > {"south", fun2}, > {"west", fun3}, > }; > > However I would like to have fun2 and fun3 corresponds to another type > of function > T::*proc(string) > > How can I do it? There are various ways, but my current favorite is listed below. It's a bit long, but at least it's complete. Here's the general idea: At compile-time: 1) Define integral constants to represent the string keys. 2) Specialize a template for each key, i.e. for each constant. Each specialization can define any kind of function-call syntax it wants. The template effectively serves as a map from compile-time values to run-time behavior. At run-time: 1) Map the run-time strings to compile-time integral constants, e.g. with an if/else tree. 2) Call the relevant function in the template instantiation that corresponds to the compile-time constant, passing down any run-time arguments. #include <iostream> #include <stdexcept> namespace ben { /* @todo Use a proper string type, rather than a pointer to a * type whose meaning is completely platform-dependent. */ typedef wchar_t const* wchar_pointer_t; /* @todo Use user-defined numeric types, rather than primitives. */ typedef int integer_t; struct foo_t { void proc(integer_t) { } }; enum direction_t { north, east, south, west }; /* A type to represent compile-time constants. An alternative * implementation would just inherit boost::mpl::integral_c. */ template<direction_t D> struct direction_c { static direction_t const value = D; }; template<direction_t D> direction_t const direction_c<D>::value; /* Map strings to directions, at run-time. */ inline direction_t parse(wchar_pointer_t const w) { if (w == std::wstring( L"north" )) return north; if (w == std::wstring( L"east" )) return east; if (w == std::wstring( L"south" )) return south; if (w == std::wstring( L"west" )) return west; throw std::invalid_argument( "bad direction string" ); } /* Map run-time values to compile-time. * * The "default" case is deliberately left out of the * switch, so the compiler can warn if any of the predefined * directions are missing. */ template<typename F> void get_constant(direction_t const d, F f) { switch (d) { case north: f(direction_c< north >( )); return; case east: f(direction_c< east >( )); return; case south: f(direction_c< south >( )); return; case west: f(direction_c< west >( )); return; } throw std::invalid_argument( "bad direction" ); } template<typename F> void get_constant(wchar_pointer_t const d, F f) { get_constant(parse(d), f); } /* Specializations could take any number and types of arguments; * however, they must have "catch-all" operator() so they are * all syntactically compatible. If you want to omit the * catch-alls, then you have to arrange the code so that the * compiler can prove that no response will ever be passed the * wrong argument type. */ template<direction_t D> struct response_c; template<> struct response_c<north> { void operator()(integer_t) const { std::cout << "north gets an integer\n"; } template<typename T> void operator()(T) const { throw std::invalid_argument( "north: wrong arg type" ); } }; template<> struct response_c<east> { void operator()(integer_t) const { std::cout << "east gets an integer\n"; } template<typename T> void operator()(T) const { throw std::invalid_argument( "east: wrong arg type" ); } }; template<> struct response_c<south> { void operator()(void (foo_t::*)(integer_t)) const { std::cout << "south gets a foo_t::*\n"; } template<typename T> void operator()(T) const { throw std::invalid_argument( "south: wrong arg type" ); } }; template<> struct response_c<west> { void operator()(void (foo_t::*)(integer_t)) const { std::cout << "west gets a foo_t::*\n"; } template<typename T> void operator()(T) const { throw std::invalid_argument( "west: wrong arg type" ); } }; template<typename A> class responder_to { A const m_argument; public: responder_to( A const a ) : m_argument( a ) { } template<typename D> void operator()(D) const { response_c<D::value>( )(m_argument); } }; template<typename A> void respond(wchar_pointer_t const d, A const arg) { get_constant(d, responder_to<A>( arg )); } } int main() { ben::respond(L"north" , 42); ben::respond(L"east" , 42); ben::respond(L"south" , &ben::foo_t::proc); ben::respond(L"west" , &ben::foo_t::proc); } -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Neil Butterworth on 3 Nov 2009 18:19 Ben wrote: > Hi I need to associate a string with a function. I define the > following table > > struct COMMAND { > const WCHAR * name; > void T::*proc(int); > }; > > static const COMMAND commands[] = { > {"north", fun0}, > {"east", fun1}, > {"south", fun2}, > {"west", fun3}, > }; > > However I would like to have fun2 and fun3 corresponds to another type > of function > T::*proc(string) > > How can I do it? The table driven approach only really works if all functions in the table have the same signature, otherwise how do you provide the different parameters needed by the different signatures? A way round this is to use different tables for the different functions - if a lookup fails in one table, you move on to the next, which has different logic for the actual function call. A third alternative is to abandon tables and use that old favourite, the if-ladder, in which case you have complete control over how the function parameters are provided. Neil Butterworth -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: tohava on 3 Nov 2009 18:18 On Nov 4, 1:28 am, Ben <wudehui2...(a)gmail.com> wrote: > However I would like to have fun2 and fun3 corresponds to another type > of function > T::*proc(string) > > How can I do it? You can't get this to work and have the compiler type-check the argument of the function for you (the reason for this is that the string used as a map key can be something only known at run-time, i.e. received from the user). One way you can do this is to have the map accept a union parameter: union Union_t { public: Union_t(std::string s) {this->s = s;} Union_t(int i) { this->i = i; } public: std::string s; int i; }; You store all your functions as functions that accept a Union_t parameter, and call them with a properly constructed Union_t parameter (note: can the union constructors do implicit conversions?). It might also be possible to use a void * as the parameter, but I'm not sure if it breaks aliasing rules or not (I'd guess not, but I do not know for sure). -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
|
Next
|
Last
Pages: 1 2 Prev: An stl list of function pointers Next: When are data structures copied |