0

I have an application where a specific message type needs to be selected based on user input (a string or int). There is a 1:1 mapping of a (string or int to a fixed type). For example 1-> int or "Apple"-> struct Apple ..and so on.

I tried to map int to a type using a template GetType<>.

struct messageA {int x;};
struct messageB {double y;};


/// Trying to map int to a type
template<int>
struct GetType{};

template<>
struct GetType<1> {
    using type = messageA;
};

template<>
struct GetType<2> {
    using type = messageB;
};
///


/// this some_task function is called from some UI
void some_task(int ID) {

    // here I can not use the ID value in GetType<  >
    typename GetType<1>::type *msg = new typename GetType<1>::type(); 
    
    some_other_generic_function(msg);

}

But it seems I can not use this, inside the function some_task because ID is a runtime argument. The switch case is an option but then every time when a new message type is needed, I need to change the switch case.

What other ways can we achieve the association from int to type? Can we create some sort of list of types and which can be indexed with int (actually I needed string, but I will deal with strings later)?

12
  • 1
    Does based on user type mean that the user specifies the type, or do you need to deduct it? Commented Aug 11, 2021 at 10:42
  • Why not use and std::map? Commented Aug 11, 2021 at 10:43
  • 1
    std::variant<messageA, messageB> seems to be what are after. Should play well with a visitor that invokes some_other_generic_function. Commented Aug 11, 2021 at 10:44
  • 1
    In that case use a factory pattern, depending on the user input you select something with the same code you should have some common basis. Otherwise it will be separate functions anyway. Commented Aug 11, 2021 at 10:46
  • 1
    The macros expanding into templates does not contradict my point. Just so happens the libraries I mentioned also resolved to template metaprogramming attempts. Commented Aug 11, 2021 at 11:09

1 Answer 1

3

Creating a type, based on some precondition, is where Creational Design Patterns are great. For this specific application I would propose a Factory method. This does mean you require a messageBase type class.

Using a base class, it becomes easy to map your type to a specific implementation using a std::map. However, you will need to expose shared properties through the base class.

struct messageBase {
  virtual std::string print() = 0;
};

struct messageA : public messageBase {
  int x{123};
  std::string print() override { return std::to_string(x); }
};

struct messageB : public messageBase {
  double y{123.321};
  std::string print() override { return std::to_string(y); }
};

I find that the mapping part is better to do with a std::map than a switch due to the ease of extention.

// Lookup for ints + str
std::map<int, std::function<messageBase*()>> int_lookup;
std::map<std::string, std::function<messageBase*()>> str_lookup;

// Using a map instead of a switch - Put this in a factory
int_lookup[1] = []() { return new messageA(); };
int_lookup[2] = []() { return new messageB(); };
str_lookup["foo"] = []() { return new messageA(); };
str_lookup["bar"] = []() { return new messageB(); };

Example usage and a working example:

// Use the maps to create the "messages" on the fly
std::cout << int_lookup[1]()->print() << std::endl;
std::cout << int_lookup[2]()->print() << std::endl;
std::cout << str_lookup["foo"]()->print() << std::endl;
std::cout << str_lookup["bar"]()->print() << std::endl;

// Output:
// 123
// 123.321000
// 123
// 123.321000
Sign up to request clarification or add additional context in comments.

3 Comments

Your solution is good, but unfortunately, I can not change the legacy messages and make them derived from the base class.
In that case you can still use this approach. However, you will need to implement the structural design pattern called the decorator. You use this to add functionality to existing classes which are closed for extention. See the updated example using a decorator.
Great, I will try your approach

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.