Introduce "typegroups" for template specialization without duplicated code

203 views
Skip to first unread message

Peter Kondratyuk

unread,
Mar 27, 2014, 1:11:37 PM3/27/14
to std-pr...@isocpp.org
Currently, when specializing templates for multiple types, each type has to have its own specialization. This requires code duplication when several of these specializations should behave identically.

Obviously, duplicated code is bad, so I propose a new keyword, "typegroup", that is only used in template specializations. This keyword would tell the compiler to specialize the template identically for each type in the typegroup. The example below is a self-explanatory demonstration of the concept:

Example:

//Proposed introduction of the "typegroup" keyword into the C++ standard
//To avoid code duplication in template specializations

typegroup floatTypes {double, float, complex};
typegroup intTypes {short, int, long long};

//Templated function, but it also could be a method of a templated class
template<typename T> void PrintValue(T value)
{
    //Do something for all types, except specialized one
    printf("No specialization for this type.\n");
}

//Specialization of the function for all floatTypes
template<> PrintValue<floatTypes>(floatTypes value)
{
    printf("%e \n", value);
}

//Specialization of the function for all intTypes
template<> PrintValue<intTypes>(intTypes value)
{
    printf("%i \n", value);
}

Billy O'Neal

unread,
Mar 27, 2014, 1:40:40 PM3/27/14
to std-proposals
It seems that specializations aren't the right tool for what you want here. Overloading already has this behavior: http://ideone.com/PHqhpZ

  1. #include <cstdio>
  2. #include <cstdint>
  3.  
  4. template<typename T>
  5. void Print(T)
  6. {
  7. std::printf("Nothing here!\n");
  8. }
  9.  
  10. void Print(std::int64_t val)
  11. {
  12. std::printf("%lld\n", val);
  13. }
  14.  
  15. void Print(double val)
  16. {
  17. std::printf("%f\n", val);
  18. }
  19.  
  20. int main() {
  21. Print(42ll);
  22. Print(42.0);
  23. Print("This is an unrelated type");
  24. }


Moreover, your printf example would be wrong; %i does not print long long values.

--

---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.

Zhihao Yuan

unread,
Mar 27, 2014, 1:58:55 PM3/27/14
to std-pr...@isocpp.org
On Thu, Mar 27, 2014 at 1:11 PM, Peter Kondratyuk <pkond...@gmail.com> wrote:
> typegroup floatTypes {double, float, complex};
> typegroup intTypes {short, int, long long};
>
> //Templated function, but it also could be a method of a templated class
> template<typename T> void PrintValue(T value)
> {
> //Do something for all types, except specialized one
> printf("No specialization for this type.\n");
> }
>
> //Specialization of the function for all floatTypes
> template<> PrintValue<floatTypes>(floatTypes value)
> {
> printf("%e \n", value);
> }
>
> //Specialization of the function for all intTypes
> template<> PrintValue<intTypes>(intTypes value)
> {
> printf("%i \n", value);
> }

Function templates can be overloaded, so you already able to

template <typename T>
auto Print(T) -> std::enable_if_t<std::is_integral<T>{}>
{
std::cout << "integer" << std::endl;
}

template <typename T>
auto Print(T) -> std::enable_if_t<std::is_floating_point<T>{}>
{
std::cout << "floating point" << std::endl;
}

int main()
{
Print(0);
Print(0.0);
}

If you want to write you own "typegroup" traits, some
meta functions sounds like "is_one_of" might help, and
such a construct can be build with the help of a typelist
or a language supported one in the future:

http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2013/n3728.html

And if you don't like the `enable_if` syntax (no one does),
concepts lite in the future can turn a type predicate into
a concept, so that you can write

template <Integral T>
void Print(T v);

The old "terse" syntax of concepts lite allows you to write the
above as:

void Print(Integral v);

But it has its own problem. We expect to see a new one.

--
Zhihao Yuan, ID lichray
The best way to predict the future is to invent it.
___________________________________________________
4BSD -- http://4bsd.biz/

Peter Kondratyuk

unread,
Mar 27, 2014, 1:59:26 PM3/27/14
to std-pr...@isocpp.org
Overloading suffers from exactly the same problem - you would need to overload the function for each type (double, float for one specialization, and int, short and long for the other) and the code would be duplicated. The printf() in the example is not a great choice, obviously; it just serves to illustrate the purpose, which is to avoid duplication for types that should behave identically in the simulation.

So i believe the point of the proposal is still correct. Do you disagree?
Peter

Billy O'Neal

unread,
Mar 27, 2014, 2:05:41 PM3/27/14
to std-proposals
No, overloading doesn't suffer that problem. Look at my example again -- I passed int, plain int, and it called the long long overload even though there was a function template of the same name accepting any T.

We can't argue that the proposal makes sense without a concrete problem it solves not already solved by other language features.

--

Peter Kondratyuk

unread,
Mar 27, 2014, 2:11:28 PM3/27/14
to std-pr...@isocpp.org
@ Zhihao

Yes, I believe that enable_if solves the same problem, but is far more complex syntactically and conceptually than a typegroup. And there's a need to use "enable_if" in combination with "is_one_of", since type tests in the std library won't cover every combination of types. So it seems to me that this problem can be solved much more elegantly with a keyword addition.

Nevin Liber

unread,
Mar 27, 2014, 2:14:44 PM3/27/14
to std-pr...@isocpp.org
On 27 March 2014 13:11, Peter Kondratyuk <pkond...@gmail.com> wrote:
Yes, I believe that enable_if solves the same problem, but is far more complex syntactically and conceptually than a typegroup. And there's a need to use "enable_if" in combination with "is_one_of", since type tests in the std library won't cover every combination of types. So it seems to me that this problem can be solved much more elegantly with a keyword addition.

Is that still true once we have Concepts Lite?
--
 Nevin ":-)" Liber  <mailto:ne...@eviloverlord.com(847) 691-1404

Zhihao Yuan

unread,
Mar 27, 2014, 2:22:16 PM3/27/14
to std-pr...@isocpp.org
I listed two proposals in my reply. With them, the whole solution
is supposed to look like:

template <typename T>
constexpr bool Float = is_one_of_v<T, <double, float, complex>>;
// is_one_of is just a simple variadic meta function
// <T...> is the proposed typelist syntax
// the whole thing is a "concept" as variable template

void Print(Float{T} v) {} // tentative new terse syntax

The program structure above looks simple enough to me.

Peter Kondratyuk

unread,
Mar 27, 2014, 2:30:12 PM3/27/14
to std-pr...@isocpp.org
@ Billy

The problem with overloads is still there! In your example it's just hidden by the ability of the compiler to upconvert int to __int64. But what if I can't have the argument be converted to the longest type, and actually need the specialization for each of those types that have to behave identically? For example, what if I am looking for the sizeof() of the argument passed to the function?

Respectfully, I don't think you understand the essence of the problem, so I'll rephrase it. Without code duplication, it's currently not possible to create specializations to behave identically for several of types (not necessarily related types). Say I need one specialization for types {int, std::string} and another specialization for types {double, complex, myType}. I saw the mention of "enable_if" and "is_one_of" metafunctions, but the syntax is pretty bad, and it generally seems like an overcomplicated solution (imho).

Peter

Peter Kondratyuk

unread,
Mar 27, 2014, 2:45:29 PM3/27/14
to std-pr...@isocpp.org
@ Zhihao

Thanks, I didn't know about this proposed new syntax... This looks like it's usable, but still feels like an overkill subjectively to me. Do you know when they plan to introduce it?


template <typename T>
constexpr bool Float = is_one_of_v<T, <double, float, complex>>; 

Tony V E

unread,
Mar 27, 2014, 2:45:49 PM3/27/14
to std-pr...@isocpp.org
On Thu, Mar 27, 2014 at 1:58 PM, Zhihao Yuan <z...@miator.net> wrote:


Function templates can be overloaded, so you already able to

template <typename T>
auto Print(T) -> std::enable_if_t<std::is_integral<T>{}>
{
    std::cout << "integer" << std::endl;
}

 
...
 
And if you don't like the `enable_if` syntax (no one does),


http://flamingdangerzone.com/cxx11/2012/06/01/almost-static-if.html  makes enable_if more concept-like:

template <typename Condition>
using EnableIf = typename std::enable_if<Condition::value, detail::enabler>::type;

template <typename T, EnableIf<is_scalable<T>>...>
T twice(T t) { return 2*t; }

Tony

Tony V E

unread,
Mar 27, 2014, 2:48:38 PM3/27/14
to std-pr...@isocpp.org

--

The real question is, once you have Concepts, do you still want "ad hoc" type-groups?  If { int, foo, bar } all behave somehow similar, there is probably a Concept lurking there.  It is probably best to actually take the time to explicitly state what the similarities are and what assumptions the templatized functions are making.

Tony

Zhihao Yuan

unread,
Mar 27, 2014, 3:06:20 PM3/27/14
to std-pr...@isocpp.org
On Thu, Mar 27, 2014 at 2:45 PM, Peter Kondratyuk <pkond...@gmail.com> wrote:
> @ Zhihao
>
> Thanks, I didn't know about this proposed new syntax... This looks like it's
> usable, but still feels like an overkill subjectively to me. Do you know
> when they plan to introduce it?

Typelist targets C++17; concepts-lite appears earlier as a TS,
later in C++17.

Even without a language supported typelist, you can always
use MPL:

template <typename T>
constexpr bool Integral = mpl::contains<mpl::vector<int, long, etc...>, T>;
// Spam Andrew to get a `concept Name = ` syntax

Anyway, it comes soon.

Zhihao Yuan

unread,
Mar 27, 2014, 3:10:22 PM3/27/14
to std-pr...@isocpp.org
On Thu, Mar 27, 2014 at 3:06 PM, Zhihao Yuan <z...@miator.net> wrote:
> template <typename T>
> constexpr bool Integral = mpl::contains<mpl::vector<int, long, etc...>, T>;

Oops, missed ::type::value. Hope a new MPL can cooperate with
C++14 better.

Billy O'Neal

unread,
Mar 27, 2014, 3:36:13 PM3/27/14
to std-proposals
>Respectfully, I don't think you understand the essence of the problem, so I'll rephrase it. Without code duplication, it's currently not possible to create specializations to behave identically for several of types (not necessarily related types).

Do you have an example that demonstrates this condition?

--

Peter Kondratyuk

unread,
Mar 27, 2014, 3:58:50 PM3/27/14
to std-pr...@isocpp.org
@Billy

Sure, there are pretty typical situations where this occurs. Here's one that I saw in production. Say you have a templated serialization function that

1) bytewise-serializes bool, short, int, long long, float, double
2) serializes char* and std::string as an four-byte integer length and a string of bytes for the content
3) Writes a error to an error log for all other types

Best you can do, with the current state of affairs,  is to specialize the function for each of the types: bool, short, int, long long, float and double. Same code, written 6 times, hence the problem!

Peter

Nevin Liber

unread,
Mar 27, 2014, 4:39:40 PM3/27/14
to std-pr...@isocpp.org
On 27 March 2014 14:58, Peter Kondratyuk <pkond...@gmail.com> wrote:
@Billy

Sure, there are pretty typical situations where this occurs. Here's one that I saw in production. Say you have a templated serialization function that

1) bytewise-serializes bool, short, int, long long, float, double
2) serializes char* and std::string as an four-byte integer length and a string of bytes for the content
3) Writes a error to an error log for all other types

Why wouldn't I just use tag dispatching or enable_if on a type_trait like is_arithmetic for (1)?  How do you implement (2) in one templated function?

I don't see why concepts lite (by C++17 or sooner) and type traits (now) won't already cover the design space.

Peter Kondratyuk

unread,
Mar 27, 2014, 5:04:46 PM3/27/14
to std-pr...@isocpp.org
@ Nevin

Why wouldn't I just use tag dispatching or enable_if on a type_trait like is_arithmetic for (1)?  How do you implement (2) in one templated function?

is_arithmetic is insufficient for (1), it will cover all arithmetic types. I need only those listed types to be included, not char or unsigned int for example. (2) will require two specializations, but it's not the point. The point is to avoid code duplication for the types listed in (1), while also keeping to requirement (3).

is_arithmetic() and similar tests are going in the right direction, but what I am saying is that there should be a way to specialize templates for a list of types, not only those types that are selectable by tests like is_arithmetic().

Cafeteria analogy: I know what I want, I want this template specialized for carrots, green peas and beef, not is_vegetable() or is_meat().

Nevin Liber

unread,
Mar 27, 2014, 5:41:45 PM3/27/14
to std-pr...@isocpp.org
On 27 March 2014 16:04, Peter Kondratyuk <pkond...@gmail.com> wrote:
is_arithmetic is insufficient for (1), it will cover all arithmetic types.

So use a type list.  Off the top of my head:

template<typename U, typename TL> 

struct in_typelist;


template<typename U, template<typename...> class TL> 

struct in_typelist<U, TL<>> : std::false_type {}; 


template<typename U, template<typename...> class TL, typename T0, typename... Ts> 

struct in_typelist<U, TL<T0, Ts...>> : in_typelist<U, TL<Ts...>> {}; 


template<typename U, template<typename...> class TL, typename... Ts> 

struct in_typelist<U, TL<U, Ts...>> : std::true_type {}; 


template<typename...>

struct types;



I need only those listed types to be included, not char or unsigned int for example. (2) will require two specializations, but it's not the point.

It is the point.  Your proposal has very limited use cases, and the moment you need to do something "slightly different", you end up having to fall back on specializations or overloading.

is_arithmetic() and similar tests are going in the right direction, but what I am saying is that there should be a way to specialize templates for a list of types, not only those types that are selectable by tests like is_arithmetic().

in_typelist<T, types<bool, short, int, long long, float, double>> pretty much accomplishes it.

Peter Kondratyuk

unread,
Mar 27, 2014, 6:24:01 PM3/27/14
to std-pr...@isocpp.org
I see that apparently it can be done, but I don't understand your code... The use case is very simple - specialize the template for types A, B, C but no other types.

Why can't this use case be accomplished without the standard library, std::true_type and some horrible syntax? I think it's a sign of a serious deficiency, when something very simple needs to be done in an unnecessarily complex way. Hence my suggestion.

Nevin Liber

unread,
Mar 27, 2014, 6:49:56 PM3/27/14
to std-pr...@isocpp.org
On 27 March 2014 17:24, Peter Kondratyuk <pkond...@gmail.com> wrote:
I see that apparently it can be done, but I don't understand your code... The use case is very simple - specialize the template for types A, B, C but no other types.

It is the same as any other type_trait.  If T is in the type list types<bool, short, int, long long, float, double> , then in_typelist derives from std::true_type; otherwise, it derives from std::false_type.  From that you can use either tag dispatching or enable_if.
 
Why can't this use case be accomplished without the standard library, std::true_type and some horrible syntax? I think it's a sign of a serious deficiency, when something very simple needs to be done in an unnecessarily complex way. Hence my suggestion.

If you think adding a language feature is simpler than a couple of lines of library code, then it is time for me to bow out of this discussion.  Good luck presenting your proposal at a future meeting. 




On Thursday, March 27, 2014 11:41:45 PM UTC+2, Nevin ":-)" Liber wrote:
On 27 March 2014 16:04, Peter Kondratyuk <pkond...@gmail.com> wrote:
is_arithmetic is insufficient for (1), it will cover all arithmetic types.

So use a type list.  Off the top of my head:

template<typename U, typename TL> 

struct in_typelist;


template<typename U, template<typename...> class TL> 

struct in_typelist<U, TL<>> : std::false_type {}; 


template<typename U, template<typename...> class TL, typename T0, typename... Ts> 

struct in_typelist<U, TL<T0, Ts...>> : in_typelist<U, TL<Ts...>> {}; 


template<typename U, template<typename...> class TL, typename... Ts> 

struct in_typelist<U, TL<U, Ts...>> : std::true_type {}; 


template<typename...>

struct types;



I need only those listed types to be included, not char or unsigned int for example. (2) will require two specializations, but it's not the point.

It is the point.  Your proposal has very limited use cases, and the moment you need to do something "slightly different", you end up having to fall back on specializations or overloading.

is_arithmetic() and similar tests are going in the right direction, but what I am saying is that there should be a way to specialize templates for a list of types, not only those types that are selectable by tests like is_arithmetic().

in_typelist<T, types<bool, short, int, long long, float, double>> pretty much accomplishes it.
--
 Nevin ":-)" Liber  <mailto:ne...@eviloverlord.com(847) 691-1404

--

---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.

Peter Kondratyuk

unread,
Mar 27, 2014, 7:06:52 PM3/27/14
to std-pr...@isocpp.org
I must admit that I don't know the process for introducing new language features. I just see the problem:

1) Specialize for type A, and no other - simple.
2) Specialize for types A, B and C, and no other - really complex, requires standard library with its type_traits, variadic templates and a syntax that reads like brainfuck code.

Zhihao Yuan

unread,
Mar 27, 2014, 9:39:26 PM3/27/14
to std-pr...@isocpp.org
On Thu, Mar 27, 2014 at 7:06 PM, Peter Kondratyuk <pkond...@gmail.com> wrote:
> I must admit that I don't know the process for introducing new language
> features. I just see the problem:
>
> 1) Specialize for type A, and no other - simple.
> 2) Specialize for types A, B and C, and no other - really complex, requires
> standard library with its type_traits, variadic templates and a syntax that
> reads like brainfuck code.

Imaging you want A, B, C, -- only if they are under 4
bytes (different arch, alignment) -- or only when they are
decayed types -- how do you add additional logic to a
typegroup? There is reason for 2) only requires 3 names
under boost::mpl. And we know the syntax (and diagnostics)
sucks, so we are trying...

If you are interested in the disambiguation techniques, you
are welcome to join/watch the talk I'm going to give on this
year's C++Now. The talk is a tutorial, and does not use MPL.

Peter Kondratyuk

unread,
Mar 28, 2014, 12:25:22 AM3/28/14
to std-pr...@isocpp.org
Thanks Zhihao,

I understand and that typegroups would have far more limited capabilities compared to Concepts Lite. It just seems like an easy fix for this particular simple use case. I appreciate your mention of MPL, I didn't know about it - I'll look into it.

Peter

Tony V E

unread,
Mar 28, 2014, 11:18:26 AM3/28/14
to std-pr...@isocpp.org



On Fri, Mar 28, 2014 at 12:25 AM, Peter Kondratyuk <pkond...@gmail.com> wrote:
Thanks Zhihao,

I understand and that typegroups would have far more limited capabilities compared to Concepts Lite. It just seems like an easy fix for this particular simple use case. I appreciate your mention of MPL, I didn't know about it - I'll look into it.

Peter


It is an "easy" solution for the use case.  However:

A - is it a common (and compelling) use case?
B - is it really easy? - conceptually, yes.  But in terms of language changes, it is not easy.  Introducing 'typegroup' as a keyword breaks any code that was using 'typegroup' as a variable or type name, etc.  (Also, is it worth the committee's time - which is very valuable but very limited, with lots of other proposals to consider)

So A needs to be really strong to overcome B.  The committee doesn't like breaking code.

And stylistically, is it worthwhile?  Do the types in the typegroup have anything more in common than "happen to *currently* have the same syntax for this function"?  I don't think common syntax is enough reason to refactor something.  I think it should be refactored when they have common semantics, and the commonality has meaning.

I had a co-worker that always ended up with base classes called "common" or "base".  There was no meaning to a class like that.  It was just "this stuff happens to be common".  If you can't name it well, then it probably doesn't represent a worthwhile abstraction, and will constantly be reworked and misused until the abstraction is found.

'typegroup' feels the same to me.  Either there is a real abstraction (like "integral" or whatever) that deserves more than just 'typegroup' (ie deserves a Concept) or the commonality is just happenstance and is prone to change, error, and maintenance issues.

Anyhow, forget my stylistic reasons, it probably doesn't pass A and B above.  I don't think it is a bad idea, and I've seen cases like you are describing, but it isn't enough for the cost.

Tony

Geoffrey Romer

unread,
Mar 29, 2014, 8:25:16 PM3/29/14
to std-pr...@isocpp.org

Code duplication is just as much of a problem for class templates as for function templates (and the workarounds are worse), but your proposal doesn't seem to do anything for them. It also doesn't help in cases where you're specializing a non-type template parameter.

I think code duplication in template specializations is a problem well worth addressing, but I'd like to see it addressed comprehensively, not by picking off ad-hoc special cases.

--

George Makrydakis

unread,
Mar 29, 2014, 9:32:00 PM3/29/14
to std-pr...@isocpp.org, Geoffrey Romer

I think that most code duplication occurs due to less than adequately usable code generation facilities.

Simply depending on a constraints - driven model (type driven pattern matching through partial specializations, sfinae, soon concepts etc) means that for every specific case scenario, there has to be some non - trivial boilerplate to be written at times in order to get the necessary code generated. Sometimes this even involves using preprocessor metaprogramming techniques that may complicate things even further, maintenance - wise.

The most brilliant example of code generation done right in C++11 is what happens when multiple parameter packs are expanded in various combinations involving them, for each of which our intent is explicit. Parameter packs made it possible to implement variadic template behaviour, avoiding underlining code duplication as was previously done in C++03 libraries implementing typelists and other constructs.

Should there be some way of extending such "expansion" mentality into code "blocks" (involving more than a single expression / statement) generation territory in a way that is easily cooperating with the conceptually implicit constraints - driven model we are using for generating code "blocks" right now, code duplication could be effectively minimized.

But this should be an argument for another thread and it is far from being an easy subject to tackle in its general case.

Farid Mehrabi

unread,
Apr 9, 2014, 12:48:05 PM4/9/14
to std-proposals
that reminds me of  'concept' proposal which was intended for c++10 but later it was dropped from the standard due to its complexity.
I see that you are proposing somewhat a much lighter version. I didn`t follow the thread to see where it ended; but I think its not too bad to take a look again.

regards,
FM.


On Thu, Mar 27, 2014 at 9:41 PM, Peter Kondratyuk <pkond...@gmail.com> wrote:
Currently, when specializing templates for multiple types, each type has to have its own specialization. This requires code duplication when several of these specializations should behave identically.

Obviously, duplicated code is bad, so I propose a new keyword, "typegroup", that is only used in template specializations. This keyword would tell the compiler to specialize the template identically for each type in the typegroup. The example below is a self-explanatory demonstration of the concept:

Example:

//Proposed introduction of the "typegroup" keyword into the C++ standard
//To avoid code duplication in template specializations

typegroup floatTypes {double, float, complex};
typegroup intTypes {short, int, long long};

//Templated function, but it also could be a method of a templated class
template<typename T> void PrintValue(T value)
{
    //Do something for all types, except specialized one
    printf("No specialization for this type.\n");
}

//Specialization of the function for all floatTypes
template<> PrintValue<floatTypes>(floatTypes value)
{
    printf("%e \n", value);
}

//Specialization of the function for all intTypes
template<> PrintValue<intTypes>(intTypes value)
{
    printf("%i \n", value);
}

--

---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.



--
how am I supposed to end the twisted road of  your hair in the dark night,
unless the candle of your face does not turn a lamp on up my way?
Reply all
Reply to author
Forward
0 new messages