Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Better way of enforcing this template?

Tags:

c++

Currently, I have a function template like this that converts a vector into a string (just a natural string, separating the elements with a comma):

//the type T must be passable into std::to_string
template<typename T>
std::string vec_to_str(const std::vector<T> &vec);

As you can see, this is only meant for vectors whose elements can be passed into the built-in std::to_string function (such as int, double, etc.)

Is it considered a good practice to document with comments the allowed T? If not, what should I do? Is it possible to enforce this in a better way?

like image 889
Dennis Ritchie Avatar asked Dec 14 '12 22:12

Dennis Ritchie


People also ask

What are the uses of templates?

A template is a document type that creates a copy of itself when you open it. For example, a business plan is a common document that is written in Word. Instead of creating the structure of the business plan from scratch, you can use a template with predefined page layout, fonts, margins, and styles.

What are different types of methods that a template method can call?

Template methods call the following kinds of operations: concrete operations (either on the ConcreteClass or on client classes); concrete AbstractClass operations (i.e., operations that are generally useful to subclasses); primitive operations (i.e., abstract operations);

How does a Word template work?

Word templates come ready-to-use with pre-set themes and styles. All you need to do is add your content. Each time you start Word, you can choose a template from the gallery, click a category to see more templates, or search for more templates online. For a closer look at any template, click it to open a large preview.


2 Answers

With static_assert and some expression SFINAE, you can have a nice compile time error message:

template<typename T>
constexpr auto allowed(int) -> decltype(std::to_string(std::declval<T>()), bool())
{
    return true;
}

template<typename>
constexpr bool allowed(...)
{
    return false;
}

template<typename T>
std::string vec_to_str(const std::vector<T>& vec)
{
    static_assert(allowed<T>(0), "Invalid value type.");
    return "";
}

struct foo {};

int main()
{
    std::vector<int> v_int;
    vec_to_str(v_int);

    std::vector<foo> v_double;
    vec_to_str(v_double);       // static_assert fires here
}
like image 193
jrok Avatar answered Sep 24 '22 12:09

jrok


Since std::to_string is a C++11 feature, I guess you might be open to a C++11 solution. In this particular case, you can use the trailing return type in a sfinae manner:

template <typename T>
auto vec_to_str(const std::vector<T>& vec) -> decltype(std::to_string(std::declval<T>()));

which would fail to substitute (and eliminate that overload) if the value-type does not work for to_string. But still, that's not necessarily the most eye-pleasing way to document and enforce the rule. There is probably a pre-C++11 version of the above Sfinae trick too, but it won't be any prettier.

In general, I would say that it's fine to simply document it in the comments (possibly with a doxygen tag, like \tparam). You could use a concept-check mechanism, in the style of Boost.Concept-Check if you want.

As a side note, for this specific case, I might recommend that you rely on the std::ostream operator << instead of the to_string function, since it is more likely that custom types (e.g., a 2D vector or something) will be equipped with an overload for outputting to a stream.

like image 28
Mikael Persson Avatar answered Sep 26 '22 12:09

Mikael Persson