3

I have a template function in C++ that basically writes values to an XML file, for validation purposes I wish to write out the variable type as well as its value. I am currently using typeid(T).name() which works great for int, double etc but I want a special case for char arrays and std::string so that it always writes out "string" or something more meaningful than:

class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >

or

char const  [2]

Any ideas on how I can do this in an elegant way?

My code (cut down) looks like this (not the function is template only)

  template <typename T> bool SetValue(const std::string &sectionName, const std::string &valueName, const T &value)
  {
      myXMl->AddAttribute(TYPE_DEF,typeid(T).name());
      return true;
  }

2 Answers 2

10

You can use a traits class for this:

template<class T>
class type_name {
public:
    static std::string get() { return typeid(T).name(); }
};

This template implements the default-case. Now you can specialize it for types that should have special names:

template<>
class type_name<std::string> {
public:
    static std::string get() { return "string"; }
};

template<>
class type_name<const char *> {
public:
    static std::string get() { return "string"; }
};

template<int N>
class type_name<const char[N]> {
public:
    static std::string get() { return "string"; }
};

Your code would then look like this:

template <typename T> bool SetValue(const std::string &sectionName, 
                                    const std::string &valueName, const T &value)
  {
      myXMl->AddAttribute(TYPE_DEF, type_name<T>::get());
      return true;
  }
Sign up to request clarification or add additional context in comments.

1 Comment

I'm not sure I follow, how do I add this to my code? I updated my question with my example code.
4

You can wrap your type inside shim functions. These functions don't actually have to be defined, since they won't be ever executed; you only need declarations to employ overloading resolution to pick the right function for you, so you can take the type of its result with typeid() expression.

Remember that template functions are only selected for perfect match, whilst overloading of non-template functions allows for implicit conversions.

Example:

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

struct string{};

template <typename T> T shim(T);
// Not needed, literals will be matched by (const char*) below
// template <size_t S> string shim(const char (&)[S]);
string shim(const char*);
string shim(const std::string&);

int main()
{
  std::cout << typeid(shim(1.0)).name() << "\n"
            << typeid(shim(true)).name() << "\n"
            << typeid(shim(0)).name() << "\n"
            << typeid(shim((void *)NULL)).name() << "\n"
            << typeid(shim("hello there")).name() << "\n"
            << typeid(shim((const char*)"hello there")).name() << "\n"
            << typeid(shim(std::string("hello there"))).name() << std::endl;
}

2 Comments

You still need a version for char [N], because if the template the OP has there is instantiated with a char [N], then it will not be a literal inside that functino, and the template-version of shim will be selected.
Yes indeed. I like your solution better, though :D I think mine could be used to "enhance" yours by employing overloading thus avoiding defining as many template classes.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.