1

I need help getting the broken part of this code working.

How do I tag dispatch two functions (that return different value-types) based on a string?

If the overall code can be simplified with the intent of dispatching with strings, please do make recommendations. TY.

Requirements:

  1. Dispatch based on a string
  2. Rectangle overload needs to return int, while Circle overload needs to return std::string
  3. The mapping from Rectangle_Type to int and Circle_Type to std::string is fixed and known at compile time. Part of my problem is std::map is a run-time construct: I don't know how to make the std::string to tag mapping a compile-time construct.
  4. If necessary, run-time resolution is okay: however, the dispatch must allow for different return types based on the enum/type resolved to.

CODE

#include <map>
#include <string>
#include <iostream>

struct Shape    { };
struct Rectangle_Type : public Shape { using value_type=int;         };
struct Circle_Type    : public Shape { using value_type=std::string; };

Rectangle_Type Rectangle;
Circle_Type    Circle;

static std::map<std::string,Shape*> g_mapping =
{
    { "Rectangle", &Rectangle },
    { "Circle",    &Circle    }
};

int tag_dispatch( Rectangle_Type )
{
    return 42;
}

std::string tag_dispatch( Circle_Type )
{
    return "foo";
}

int
main()
{
    std::cerr << tag_dispatch( Circle    ) << std::endl;   // OK
    std::cerr << tag_dispatch( Rectangle ) << std::endl;   // OK

#define BROKEN
#ifdef BROKEN
    std::cerr << tag_dispatch( (*g_mapping["Rectangle"]) ) << std::endl;
    std::cerr << tag_dispatch( (*g_mapping["Circle"])    ) << std::endl;
#endif
}
kfmfe04
  • 14,936
  • 14
  • 74
  • 140

2 Answers2

1

Unless C++11 has changed this.. The problem is that you are dereferencing a Shape* pointer, which means the resulting datatype (Shape&) does not have a valid overload of tag_dispatch.

You can do something like g_mapping["Rectangle"]->tag_dispatch(). Or more cleanly rewrite as below.

std::string tag_dispatch( Shape& shape)
{
    return shape->tag_dispatch();
}

This way you can support non Shape objects with an identical interface. Both need you to make tag_dispatch as a virtual function of Shape as well.

Karthik T
  • 31,456
  • 5
  • 68
  • 87
  • Yes, I realize that is the problem - is there some other technique I can use which will allow me to effectively tag dispatch based on a string? – kfmfe04 Jan 30 '13 at 02:39
  • @kfmfe04 `g_mapping["Rectangle"]->tag_dispatch()` would work if you add it as a virtual function. – Karthik T Jan 30 '13 at 02:41
  • almost there! How do I deal with the variant return values (Rectangle's version returns an int, but Circle's returns a string) if I use a virtual function? Maybe using templates somehow? – kfmfe04 Jan 30 '13 at 02:42
  • @kfmfe04 "42"? I cant think of a neat way to do that.. You could write another overload with `Rectangle_Type&` as well.. – Karthik T Jan 30 '13 at 02:44
  • 1
    @kfmfe04 [tag dispatching](http://efesx.com/2010/01/03/tag-dispatching/) seems to be a purely compile time technique. Do you need to use it this way? – Karthik T Jan 30 '13 at 02:50
  • In my RL code, I have some ugliness (10 maps that return different value-types via boost::variant based on a string lookup) that I want to get rid of. If I can tag dispatch based on those strings, then I can get rid of boost::variant and do the exact map lookup. One important note though: the string to tag mapping is fixed/known at compile-time, but I don't know how to manifest this without using a std::map (a runtime construct). – kfmfe04 Jan 30 '13 at 02:55
  • @kfmfe04 you can try to use [compile time string compare](http://stackoverflow.com/questions/5721813/compile-time-assert-for-string-equality) as shown by the answerer there, along with some template techniques to try and achieve this is proper compile time. You can add that as another question if you feel that might be useful. – Karthik T Jan 30 '13 at 07:40
0

C++ does not have dynamic dispatch. This is probably what you expect to happen. You can emulate it with dynamic_cast, but that's slow and not recommended. You could use a virtual function returning an enum for the type though.

class base
{
public:
    virtual ~base() // Don't forget the virtual destructor if you want to inherit from it!
    {}

    enum type
    {
        a, 
        b, 
    };

    virtual type get_type() const = 0;
};

class a : public base
{
public:
    virtual type get_type()
    {
        return base::type::a;
    }
};

class b : public base
{
public:
    virtual type get_type()
    {
        return base::type::b;
    }
};
cooky451
  • 3,460
  • 1
  • 21
  • 39
  • I was thinking about using virtuals, but one of my requirements is making Rectangles return an int and a Circle return a string. Not sure how to fit that in within the context of virtuals. – kfmfe04 Jan 30 '13 at 02:45
  • I believe [virtual functions do implement dynamic dispatch](http://en.wikipedia.org/wiki/Dynamic_dispatch#C.2B.2B_Implementation). What you want to say is that C++ does not have dynamic dispatch ON method parameters. – Karthik T Jan 30 '13 at 02:45
  • @kfmfe04 That won't work that easily in C++. The problem is, again, that it doesn't have dynamic dispatch. (The output function overload would need to dynamically determine the type as well.) So, as I said, the only way to do this that I see is using a virtual function which returns an enum for the type and than implementing all the dispatch by yourself. – cooky451 Jan 30 '13 at 02:54
  • @cooky451 could you briefly sketch a code snippet to show me how this can be done? ty. – kfmfe04 Jan 30 '13 at 03:01
  • @kfmfe04 I edited my answer. You can now take a base& or base* and switch () on get_type. But beware, if you have something like animal<-mammal<-tiger, than the thing can just be a tiger, not a mammal _and_ a tiger. So, this method is somewhat limited, but it'll probably work just fine for you. – cooky451 Jan 30 '13 at 03:25