简体   繁体   中英

Function template specialization to force enum value with type

A function is required to send messages, given a message type and a data structure of a type specific to the message type:

enum class MsgType
{
    msgInt,
    msgDouble,
    msgString
};

template < some template here >
void sendMessage(MsgType type, T value);

I expect to call this function in the following way:

sendMessage( MsgType::msgInt, 42 );
sendMessage( MsgType::msgString, "Answer to the life, etc,etc.");
//sendMessage( MsgType::msgDouble, "Should not compile");
//sendMessage<MsgType::msgString>( "That is easy!" );

How could I implement the previously described template function specialization?

Note: If possible, with C++11, but C++14 is also acceptable.

EDITED:

The current implementation, which only accept the MsgType as template parameter (not as function parameter).

template<MsgType Id, typename T>
void sendMessage(T data);

template<>void sendMesssage<MsgType::msgNumCar, int>( int data ){ //...   }
template<>void sendMesssage<MsgType::msgInt, int>( int data ){ //...   }
template<>void sendMesssage<MsgType::msgString, string>( string data ){ //...   }
// A call
sendMessage<MsgType::msgNumCar>( 42 );

Templates can have constant parameters which are known in compile-time. You could do the following:

enum MsgType {
    msgInt,
    msgDouble,
    msgString
};

template<int>
struct Message { };

template<>
struct Message<MsgType::msgInt> {
    static void send(int value) { cout << "int: " << value; }
};

template<>
struct Message<MsgType::msgDouble> {
    static void send(double value) { cout << "double: " << value; }
};

template<>
struct Message<MsgType::msgString> {
    static void send(const char* value) { cout << "string: " << value; }
};

And invoke it:

Message<MsgType::msgInt>::send(5);
Message<MsgType::msgDouble>::send(3.14);
Message<MsgType::msgString>::send("hello");

// will not compile
//Message<MsgType::msgDouble>::send("should not compile");

notice that the template parameter must be a constant (ie known at compile time). Which means that the following code won't compile:

int type = MsgType::msgInt;
Message<type>::send(123);

But, why won't you just create 3 overloads for sendMessage ?

Instead of validating that the user passes the correct message type enum value into the send function, prevent it entirely with some traits:

#include <iostream>

enum class MsgType
{
    msgInt,
    msgDouble,
    msgString
};

template <typename T>
struct MsgTypeOf;

template <>
struct MsgTypeOf<int>
{
    static MsgType msg_type;
};

template <>
struct MsgTypeOf<double>
{
    static MsgType msg_type;
};

template <>
struct MsgTypeOf<const char*>
{
    static MsgType msg_type;
};

MsgType MsgTypeOf<int>::msg_type = MsgType::msgInt;
MsgType MsgTypeOf<double>::msg_type = MsgType::msgDouble;
MsgType MsgTypeOf<const char*>::msg_type = MsgType::msgString;

template <typename T>
int sendMessage(T value)
{
    return static_cast<int>(MsgTypeOf<T>::msg_type);
}

int main()
{
    std::cout << sendMessage(42) << std::endl;
    std::cout << sendMessage("Answer to the life, etc,etc.") << std::endl;
    std::cout << sendMessage(42.42) << std::endl;
}

Output: 0 2 1

Unless I am totally off base...

You can have three overloads of sendMessage and receiveMessage .

void sendMessage(int m);
void sendMessage(double m);
void sendMessage(std::string const& m);

and

void receiveMessage(int& m);
void receiveMessage(double& m);
void receiveMessage(std::string& m);

There is no need for an enum or a function template.

Update, in response to OP's comment

My suggestion:

  1. Create struct s to be used as tags instead of an enum .
  2. Overload sendMessage and receiveMessage using the tag struct s.

struct int_message_t {};
struct double_message_t {};
struct string_message_t {};
struct username_message_t {};
struct numcars_message_t {};
struct bigstruct_message_t {};

And then overload them using

void sendMessage(int_message_t, int m);
void sendMessage(double_message_t, double m);
void sendMessage(string_message_t, std::string const& m);
void sendMessage(username_message_t, std::string const& m);
void sendMessage(numcars_message_t, int m);
void sendMessage(bigstruct_message_t, big_struct const& m);

and

void receiveMessage(int_message_t, int& m);
void receiveMessage(double_message_t, double& m);
void receiveMessage(string_message_t, std::string& m);
void receiveMessage(username_message_t, std::string& m);
void receiveMessage(numcars_message_t, int& m);
void receiveMessage(bigstruct_message_t, big_struct& m);

If you can use a function template to make the implementations of these functions, that will be nice but I would still leave that as an implementation detail.

Preserving the exact method call you presented cannot give compilation error on the wrong enum type, as the enum type sent is known only at runtime (as a method argument). And you cannot overload on function parameter value (only on type).

Getting the enum as a template parameter ( @IdanYadgar ) allows check at compile time.

Overloading on type ( @MarkB and @RSahu - using two different approaches) is also a nice option.

I believe @RSahu is the closest you can get to what you asked for. Maybe you can add a static const int value for each struct so it will actually be used as enum .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM