I have a submit table in C ++ code. It binds tags to functions that can handle these tags. In the first version, it takes functions that take two strings and return a string. Lines are serialized protobuffs.
map<string, function<string(const string& serialised_1, const string& serialised_2)>> converters = { ... { 'dog', ProcessTwoDogs }, { 'cat', ProcessTwoCats }, ... };
Here, the converter functions look like this:
string ProcessTwoDogs(const string& dog_1_str, const string& dog_2_str);
After implementing a number of these converters, I realized that they often exceed half the pattern: error checking, deserialization, serialization, etc. Therefore, I wrote a quick template that greatly simplifies my code:
template <typename ProtoT> std::string ConvertProtos( const std::string& proto_str_a, const std::string& proto_str_b, std::function<ProtoT(const ProtoT&, const ProtoT&)> convert_proto) { ProtoT proto_a = ...; ProtoT probo_b = ...;
And now convert_proto() might look like this:
Dog ProcessTwoDogs(const Dog& dog_1, const Dog& dog_2) { ... }
This is very nice, but now I have broken the dispatch table, because each animal processor has a different signature, because Dog and a Cat are proto-bufs, but are not connected otherwise. I do not know how to make a send table without resorting to a long fragment of if ... else if ....
What I want is such a card:
Then my function using a dispatch table that currently says something like
auto processor = the_map.at(tag); string new_string = processor(string_1, string_2);
becomes
auto processor = the_map.at(tag); string new_string = ConvertProtobufs(string_1, string_2, processor);
Of course, one way would be to define an abstract base class with operator() , which takes strings and then implements an instance of this class for each of my conversion functions. operator() calls some function that is defined only in derived classes. But now I have lost any gain in readability or conciseness that I could find.
Any suggestions?
Update
Following the line of reasoning suggested by @felix, I wrote the following:
#include <functional> #include <iostream> #include <map> #include <string> using std::cout; using std::function; using std::endl; using std::map; using std::string; struct Dog { void operator()() { cout << "I am a dog." << endl; } }; struct Cat { void operator()() { cout << "I am a cat." << endl; } }; string cat = string("cat"); string dog = string("dog"); template<string& s> void fn() { cout << "I am lost" << endl; } template<> void fn<dog>() { Dog dog; dog(); } template<> void fn<cat>() { Cat cat; cat(); } int main(int argc, char *argv[]) { (void)argc; (void)argv; fn<dog>(); fn<cat>(); // Oops, it all falls apart here: string dog1("dog"); fn<dog1>(); // Doesn't compile, and a dog is not a dog1. }
The problem is that the template arguments must, of course, be known at compile time. This is great when I use const string from the ontology of strings, but it fails if the strings go through the database, and therefore the search is performed dynamically based on the value, not the object.