5

I have two c++ programs that need to have a map type -> int that is known at compile time and equal between the two programs. Furthermore, I'd like to automatically make sure at compile time that the map is one-to-one. How would you solve that? (c++0x-extensions are allowed). The first part is easy: Share a

template < typename T > struct map;
template <> struct map <...> { enum { val = ...; }; };

between the programs. (The second part means that I don't want to accidently define the same val for two different types somewhere in my programs.)

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
Thomas
  • 1,001
  • 1
  • 10
  • 20
  • May I ask what this mapping is for? – sellibitze Sep 18 '10 at 12:41
  • possible duplicate of [Template metaprogram converting type to unique number](http://stackoverflow.com/questions/1708458/template-metaprogram-converting-type-to-unique-number) – kennytm Sep 18 '10 at 12:42
  • The map will be used to communicate arbitrary data between a sender and a receiver program (that's also why this question is no duplicate - it involves two programs that need to share the same map). – Thomas Sep 18 '10 at 12:44
  • Note that you can prove that this cannot be done completely generically. Imagine you have a `template struct foo;` Specializations of `foo` need `ULONG_MAX` as many numbers as `map` can represent. Or just imagine `map>` – Johannes Schaub - litb Sep 18 '10 at 12:47
  • That's okay, I will use this with a fixed and small number of classes (that might be specializations of templated classes). – Thomas Sep 18 '10 at 12:49
  • Perhaps one could use a boost::mpl list of those types and somehow use their index in the list as id? (I haven't used mpl before). – Thomas Sep 18 '10 at 12:51
  • @Thomas: if this is serialization/deserialization, please tag (and reword) the question accordingly. Type ids are not the only mean to accomplish that. And note that "communicating something arbitray" is a sure road to hell. For that there are concrete means like communication protocols. And they are called "protocols" for a reason. – Dummy00001 Sep 18 '10 at 12:56
  • @Thomas since you allow c++0x extensions, I thought adding [c++0x] is appropriate for those who hilight that tag in their list. – Johannes Schaub - litb Sep 18 '10 at 13:21
  • This isn't really a duplicate of http://stackoverflow.com/questions/1708458/template-metaprogram-converting-type-to-unique-number . Because the other question asks for *generating* the number. This question is fine with just supplying them manually but guaranteeing unique numbers. – Johannes Schaub - litb Sep 18 '10 at 23:35

4 Answers4

9

One way to ensure uniqe ids is to abuse friend function definitions

template<int N>
struct marker_id {
  static int const value = N;
};

template<typename T>
struct marker_type { typedef T type; };

template<typename T, int N>
struct register_id : marker_id<N>, marker_type<T> {
private:
  friend marker_type<T> marked_id(marker_id<N>) {
    return marker_type<T>();
  }
};

template<typename T>
struct map;

template<>
struct map<int> : register_id<int, 0> { };

// The following results in the following GCC error
// x.cpp: In instantiation of 'register_id<float, 0>':
// x.cpp:26:43:   instantiated from here
// x.cpp:14:29: error: new declaration 'marker_type<float> marked_id(marker_id<0>)'
// x.cpp:14:29: error: ambiguates old declaration 'marker_type<int> marked_id(marker_id<0>)'
//
//// template<>
//// struct map<float> : register_id<float, 0> { };
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
3

How about using boost::mpl::map? Share something like:

// Include your headers

using namespace boost::mpl;
typedef map<
      pair<1,MyFirstClass>
    , pair<2,MySecondClass>
    , pair<3,MyThirdClass>
    > m;
metal
  • 6,202
  • 1
  • 34
  • 49
2

They don't do it strictly at compile time, but this pair of functions will automatically generate a unique ID for each type passed to them:

template<class T>
int generate_type_id() {

    static int value = 0;
    return value++;

}

template<class T>
int type_id() {

    static int value = generate_type_id<T>();
    return value;

}

And you should be able to ensure that the two applications share the same identifiers for a given type by explicitly invoking the functions in order in both projects:

type_id<int>();
type_id<Foo>();
type_id< map<string, pair<int, Bar> >();

Yes, this forces you to write a list of all involved types, but you can toss it in a header, #include it between them, and at least avoid code duplication. This also absolves you of having to come up with unique IDs for each type on your own, as Johannes Schaub's answer does, though his has the advantage of being done completely at compile-time and thus statically checked by the compiler. I'm only offering an alternative.

Jon Purdy
  • 53,300
  • 8
  • 96
  • 166
  • 2
    This does not work. When you have `generate_type_id` templated by `T` it will always return 0 (http://ideone.com/Ph3ha). But if you make it a non templated function it should work (http://ideone.com/zIIGb). Though it is cumbersome to ensure the same order. – Nobody moving away from SE Sep 20 '12 at 09:44
0

An easy approach might be to just share the same class in both programs.

Re-use is one of the objectives to OOP.

A class that encapsulates the map and its initialization can be created and then used in both C++ programs.

skimobear
  • 1,188
  • 10
  • 12