MoustacheSpy
MoustacheSpy

Reputation: 763

C++ How can I predefine a constructor parameter when specializing a template for a "new" type

I have a template class that takes a name in its constructor. The name is supposed to be the name of the actual type, so that it can be put into a human-readable list or something.

Anyways I am currently defining the types like this:

typedef templateClass<someType> someTypeClass;

However, in order for the name to be also "someTypeClass" I would need the user to always specify that name correctly. Rather I want some way to force the constructor parameter to always be "someTypeClass" (FOR THAT SPECIFIC TYPE SPECIALISATION).

The purpose of the name parameter is to have a way of knowing the name of a template specialisation. Here a example

template<class t>
class TemplateClass{
private:
    std::string name;
    t data;
public:
    TemplateClass(const char* name);
    /*Irrelevant*/
};
typedef TemplateClass<int> intType; //name should be "intType" but how can I force the name to be "intType" for this case? I dont want the user to have to type "intType" for every instance he wants to make
typedef TemplateClass<char> charType; //name should be "charType"

Is this achieveable in some viable way or is there a better way to go about this?

Upvotes: 1

Views: 238

Answers (3)

super
super

Reputation: 12968

You can make the name static, and initialize it yourself.

template<class t>
class TemplateClass{
private:
    static const std::string name;
    t data;
public:
    TemplateClass();
    /*Irrelevant*/
}

typedef TemplateClass<int> intType;
template <>
const std::string TemplateClass<int>::name = "intType";

typedef TemplateClass<char> charType;
template <>
const std::string TemplateClass<char>::name = "charType";

Note that with this approach you you probably want to put std::string TemplateClass<int>::name; in your header and the initialization in your .cpp

Clarifying my note with and example

TemplateClass.hpp

#include <string>

template<class t>
class TemplateClass{
private:
    static const std::string name;
    t data;
public:
    TemplateClass();
    /*Irrelevant*/
}

typedef TemplateClass<int> intType;
template <>
const std::string TemplateClass<int>::name;

typedef TemplateClass<char> charType;
template <>
const std::string TemplateClass<char>::name;

TemplateClass.cpp

#include "TemplateClass.hpp"

template <>
const std::string TemplateClass<int>::name = "intType";

template <>
const std::string TemplateClass<char>::name = "charType";

Here we are declaring the specialization variable in the .hpp but ony initializing it in the .cpp. Otherwise it will be initialized in every compilation unit that you include it in, and will get multiple definition errors.

Additional note

If you are using C++17 you can use the new inline variable and do

template <>
inline const std::string TemplateClass<int>::name = "intType";

with no need to add anything in the .cpp file.

Upvotes: 1

jfMR
jfMR

Reputation: 24778

Declaring the data member name as static as this answer suggests and assuming a compiler with C++11 support (to be able to use template alias):

#include <string>

template<class t>
class TemplateClass{
private:
    static const std::string name;
    t data;
public:
    TemplateClass();
    /*Irrelevant*/
};

#define DEFINE_COMPONENT(type, typename) \
template<> \
const std::string TemplateClass<type>::name = #typename; \
using typename = TemplateClass<type>

DEFINE_COMPONENT(int, intType);
DEFINE_COMPONENT(char, charType);

The preprocessor macro DEFINE_COMPONENT does the job of both creating the corresponding template alias with the name passed as the second argument and initializing the string member name for corresponding specialization.

Upvotes: 0

luk32
luk32

Reputation: 16080

What you look for requires reflection and currently c++ has very limited options in this regard.

There is RTTI and typeinfo that's basically it when it comes to current standard. Whether or not it is useful enough I'll leave up to you. Here is the example how can you bind textual info about the type to the class:

#include <iostream>
#include <typeinfo>
#include <string>

using namespace std;

template <typename T>
struct A {
    const char* name = typeid(T).name();
};

int main() {
    A<int> a_int;
    A<double> a_double;
    A<string> a_string;
    cout << a_int.name << "\n";
    cout << a_double.name << "\n";
    cout << a_string.name << "\n";
    return 0;
}

So there are two at least three immediate drawbacks, the link explain them a bit:

  1. It doesn't work for every type. type_info::name
  2. The field is not static, though one might expect it to be. It is evaluated at run time. This coincides with 1. constexpr-and-rtti
  3. The names are mangled. Unmangling the result of std::type_info::name

I don't think you can do much better at this point of time. Some reflection proposals are said to be evaluated but it's a rather distant future IMHO.

Upvotes: 0

Related Questions