In my real code I am parsing a series of bytes that are consecutive messages. An integer in a message header determines what is the appropriate way to interpret the bytes
To simplify for SO assume the following:
1
means use struct MsgA
2
means use struct MsgB
3
means use struct MsgC
Also for SO assume that a class, NamePrinter
is responsible for:
MsgA
, MsgB
, or MsgC
)I know how to write code for this. I also know how to switch out NamePrinter
with another class that also performs actions on messages. What I do not do not know how to do is chose which member function of NamePrinter
(or another class) to call in a more general case
#include <iostream>
// Some made up "message" structs
struct MsgA final {
static constexpr int getId() { return 1; }
const char* getName() const { return "MsgA"; }
const char* getSomethingElse() const { return "SomethingElseA"; }
};
struct MsgB final {
static constexpr int getId() { return 2; }
const char* getName() const { return "MsgB"; }
const char* getSomethingElse() const { return "SomethingElseB"; }
};
struct MsgC final {
static constexpr int getId() { return 3; }
const char* getName() const { return "MsgC"; }
const char* getSomethingElse() const { return "SomethingElseC"; }
};
// A class that creates a "message" and performs and action on it
class NamePrinter final {
public:
template <class T>
void onMsgPrintName() const {
T t;
std::cout << t.getName() << std::endl;
}
template <class T>
void onMsgPrintSomethingElse() const {
T t;
std::cout << t.getSomethingElse() << std::endl;
}
};
// A function that can convert an integer into a "message" at compile time but
// is hardcoded to call onMsgPrintName
template <class T>
void intToPrintName(const T& a_caller, const int a_id) {
switch (a_id) {
case MsgA::getId(): a_caller.template onMsgPrintName<MsgA>(); break;
case MsgB::getId(): a_caller.template onMsgPrintName<MsgB>(); break;
case MsgC::getId(): a_caller.template onMsgPrintName<MsgC>(); break;
default:
std::cout << "Runtime error" << std::endl;
}
}
// This is invalid but tries to convey that onMsgPrintName has been replaced
// with a template
//template <class F, class T>
//void intToFunc(const T& a_caller, const int a_id) {
// switch (a_id) {
// case MsgA::getId(): a_caller.template F<MsgA>(); break;
// case MsgB::getId(): a_caller.template F<MsgB>(); break;
// case MsgC::getId(): a_caller.template F<MsgC>(); break;
// default:
// std::cout << "Runtime error" << std::endl;
// }
//}
int main(int argc, char** argv) {
const NamePrinter np;
// This will end up calling NamePrinter::onMsgPrintName
intToPrintName(np, argc);
// I want something like this that allows me to pick what is called
//intToFunc<NamePrinter::onMsgPrintName >(np, argc);
//intToFunc<NamePrinter::onMsgPrintSomethingElse>(np, argc);
return 0;
}
What I would like to know is if I can replace onMsgPrintName
inside of intToPrintName
with an arbitrary member function of my choice. The commented out code shows an attempt to do this which does not compile
Add another level of indirection - a wrapper that knows how to call a particular method on any object. Along the lines of
class OnMsgPrintNameCaller {
template <typename Param, typename Obj, typename... Args>
static auto Call(Obj&& obj, Args&&... args) {
return std::forward<Obj>(obj).template onMsgPrintName<Param>(
std::forward<Args>(args)...);
}
};
And similar class for onMsgPrintSomethingElse
. Now pass this as an extra parameter to intToPrintName
:
template <typename MethodCaller, typename T>
void intToPrintName(const T& a_caller, const int a_id) {
switch (a_id) {
case MsgA::getId(): MethodCaller::template Call<MsgA>(a_caller); break;
// ...
}
}
Usage:
intToPrintName<OnMsgPrintNameCaller>(np, argc);