I have to deal with Google Protobug packets defined in various namespaces:
// Those are generated classes
/// @file server.pb.h
namespace Server {
class ServerMsg1 : public ::google::protobuf::Message {
};
class ServerMsg2 : public ::google::protobuf::Message {
};
// etc..
}
/// @file client.pb.h
namespace Unit {
class UnitMsg1 : public ::google::protobuf::MessageLite {
};
// etc..
}
The idea is, that printProtoDatawould be able to find printData in apparopriate namespace or it would choose the overload that uses a superclass of its argument (Message or MessageLite).
while I have certain generalised classes which use them as components within global namespace. Provided that I have a function\adapter (agnostic of namespace, thanks to ADL), but it must be very general to support non-Protobuf types:
///@file packet.h
// No printData function is defined, so it would be searched in terms of ADL
template <class PacketType>
void printProtoData(const PacketType &p) {
printData(p);
}
And an abstract interface class for data with a CRTP as a mix-in interface (for reasons)
// simplified user class
template<typename T, typename M>
class ProtoContainer : AbstractContainer {
M msg;
public:
// cannot be virtual, it vould instantiate printProtoData immediately
void print() { ::printProtoData(this->msg); }
};
And each namespace got a constellation of functions to print them, but some may be missing, temporarily or intentionally:
#include "packet.h"
#include "wrapper.h"
#include "server.pb.h"
#include "client.pb.h"
namespace Server {
using printFunctions::printData;
void printData(const ServerMsg1& msg);
void printData(const ServerMsg2& msg);
}
namespace Unit {
using printFunctions::printData;
void printData(const UnitMsg1& msg);
}
// An example of user for data, it instantiates printProtoData
struct ProtoUser : ProtoContainer<ProtoUser, Server::ServerMsg1 > {
// ...
};
To avoid errors, I want to define fallback functions which would take ANY type of Protobuf message, so I defined those and added using printFunctions::printData; to respective namespaces:
/// @file wrapper.h
#include <iostream>
// Can I do better than this?
namespace google {
namespace protobuf {
class MessageLite;
class Message;
}
}
// Scary SFINAE syntax because this should compile in TR1 (VS2010)
namespace printFunctions {
template <typename Msg>
typename std::enable_if<std::is_base_of<::google::protobuf::MessageLite, Msg>::value>::type
printData(const Msg& msg)
{
std::cout << "------------ " << msg.GetTypeName();
}
template <typename Msg>
typename std::enable_if<std::is_base_of<::google::protobuf::Message, Msg>::value>::type
printData(const Msg & msg)
{
std::cout << "------------ " << msg.GetTypeName();
std::cout << msg.DebugString();
}
}
This works in Vs2017+. Is this portable, i.e.
- Is this correct SFINAE style for Vs2010 (incomplete C++11)?
- Is this approach portable to GCC and clang?
VisualStudio's compiler is rather lenient and doesn't respect ADL limitations (it expands to other namespaces, including global one), so "it works here" doesn't tell me much.