From: Ben on
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
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
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
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
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! ]