Multiple (partial)specialization

108 views
Skip to first unread message

Dejan Milosavljevic

unread,
Mar 20, 2018, 5:20:40 AM3/20/18
to ISO C++ Standard - Future Proposals
Hello

  Here is simple idea how to use existing code to make several (partial) specialization at once.
  Instead of one type use list of types.

  Quick example:
template< typename T > //Primary template as we know
  class A {
     void print(){ std::cout << "Primary template" << std::endl; }
   };
template<>
  class A< { char*, std::string, std::wstring } > { // Instead one type it is list of types.
     void print(){ std::cout << "String  specialization" << std::endl; }
   };


More details/examples in attachment.

D.
 
multi_spec.html

Barry Revzin

unread,
Mar 20, 2018, 6:52:06 AM3/20/18
to ISO C++ Standard - Future Proposals


On Tuesday, March 20, 2018 at 9:20:40 AM UTC, Dejan Milosavljevic wrote:
Hello

  Here is simple idea how to use existing code to make several (partial) specialization at once.
 

These are explicit specializations.

 
  Instead of one type use list of types.

  Quick example:
template< typename T > //Primary template as we know
  class A {
     void print(){ std::cout << "Primary template" << std::endl; }
   };
template<>
  class A< { char*, std::string, std::wstring } > { // Instead one type it is list of types.
     void print(){ std::cout << "String  specialization" << std::endl; }
   };


More details/examples in attachment.

D.
 

What do you think instead of a syntax which allows you to make a partial class template specialization based on constraining the template parameter? 

template <typename T>
class A { ... };

template <typename T, typename... Us>
inline constexpr bool is_any_of_v = (std::is_same_v<T, Us> || ... );

template <typename T>
    requires is_any_of_v
<T, char*, std::string, std::wstring>
class A<T> { ... };

 Or even:

template <typename T>
concept StringLike = /* something sane */;

template <StringLike T>
class A<T> { ... };

This language feature is called Concepts, and is already in the working draft for C++20.

Dejan Milosavljevic

unread,
Mar 20, 2018, 7:06:41 AM3/20/18
to std-pr...@isocpp.org
> This language feature is called Concepts, and is already in the working draft for C++20

Concept allows you to make constraints on not (yet) exiting types.
This make specialization on existing types.



--
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-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/90cc934f-8532-4110-8d73-2824dabd51ec%40isocpp.org.

Richard Hodges

unread,
Mar 20, 2018, 7:06:58 AM3/20/18
to std-pr...@isocpp.org
At the moment, one idiomatic solution to multiple partial specialisation would be template pattern matching.

Here's the problem expressed this way:

#include <iostream>
#include <utility>

template<typename T, typename MatchCondition = void>
struct A
{
void print()
{
std::cout << "Primary template" << std::endl;
}
};

template<class T> struct is_stringlike : std::false_type {};
template<> struct is_stringlike<const char*> : std::true_type {};
template<> struct is_stringlike<std::string> : std::true_type {};
template<> struct is_stringlike<std::wstring> : std::true_type {};

template<typename T>
struct A<T, std::enable_if_t<is_stringlike<T>::value>>
{
void print()
{
std::cout << "String specialization" << std::endl;
}
};

int main()
{
auto a = A<int>();
a.print();

auto b = A<const char*>();
b.print();
}

Granted it involves some understanding of template meta-programming, but it's pretty readable, no?

R

 
--
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.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/910830df-1a1c-4e32-8358-d24a4636d1b1%40isocpp.org.

Multiple (partial)specialization

  Document number:
Date:
Reply to:


2018/02/12
Dejan D.M. Milosavljevic
(dmilos at gmail dot com)
 
Table of Contents.
  • I. Introduction
  • II. Motivation and Scope
  • III. Solutions
  • IV. Design Decisions
  • V. Impact On the Standard
  • VI. References
I. Introduction
Multiple (partial)specialization. This feature will allow multiple (partial)specialize of template without need for additional code. It is assumed that extra/additional code is something that comes after class definition or (partial) specialization.

II. Motivation and Scope
Usual problem with template specialization is how to reuse existing code for other type. One of idea is to have separate code for each type that will point to previous specialization. But this idea will rise more requests such as modification of existing functions and members. This idea avoid those request by use of existing code at point of (partial)specialization.

It is often that some types can be grouped in manner that share same class specialization. In presented example we have integer group( short, int, long ), rational ( float, double, long double ) and strng ( char*, std::string, std::wstring ).
Problem 1:
Lets make several groups of types. Each group heed their own specialization.
In next example we have two groups.
First group contain: char*, std::string, std::wstring. Second group: float, double, long double.
Note: This example can be extended with more groups like signed integers, unsigned integers.


[Example:
template< typename T >
 class A {
   void print(){ std::cout << "Primary template" << std::endl; }
  };

template<>
 class A< std::string > {
   void print(){ std::cout << "String  specialization" << std::endl; }
  };
template<>
 class A< char* > { // NOTE: In here we keep "String specialization".
   void print(){ std::cout << "String  specialization" << std::endl; }
  };

template<>
 class A< std::wstring > { // NOTE: In here we keep "String specialization".
   void print(){ std::cout << "String  specialization" << std::endl; }
  };

template<>
 class A<float > {
   void print(){ std::cout << "Rational specialization" << std::endl; }
  };
template<>
 class A< double > { // NOTE: Same as foat
   void print(){ std::cout << "Rational  specialization" << std::endl; }
  };

template<>
 class A< long double > { // NOTE: Same as foat
   void print(){ std::cout << "Rational  specialization" << std::endl; }
  };
end example]
Problem 2:
Lest start with some enum with enough large number of enumerants.
Goal is to specialize class E so the enum subgroups have same template specialization.
In this example first subgroup are consisted from red, green, blue.
Second subgroup: cyan, yellow and magenta.


[Example:
enum colors{ red, green, blue, cyan, magenta, yellow, orange, pink };

template< enum Colors color = red >
 class E {
    void print(){ std::cout << "Primary template with red default"<< std::endl;  }
  };

template< >
 class E< green > { // In here we want to keep primary definition of print function
    void print(){ std::cout << "Primary template with red default"<< std::endl;  }
  };

template< >
 class E< blue > { // In here we want to keep primary definition of print function
    void print(){ std::cout << "Primary template with red default"<< std::endl;  }
  };

template< >
 class E< yellow > { // Brand new specialization of print function
    void print(){ std::cout << "YELLOW"<< std::endl;  }
  };

template< >
 class E< magenta > { // Same as E< yellow >
    void print(){ std::cout << "YELLOW"<< std::endl;  }
  };

  // etc
end example]
Problem 3:
Similarly like for enums, in here some groups of integers need to share same specialization. Specialize for specific integer values.

[Example:
template< unsigned number>
 class I {
    void print(){ std::cout << "Primary template." << std::endl; }
  };

template< >
 class I< 0 > {
    void print(){ std::cout << " up to 10 and 10 ." << std::endl; }
  };
 /* In here repeat above code with numbers between 1 and 10*/
template< >
 class I< 10 > {
    void print(){ std::cout << " up to 10 and 10." << std::endl; }
  };

template< >
 class I< 11 > {
    void print(){ std::cout << " up to 100 and 100." << std::endl; }
  };
 /* repeat above code with numbers between 11 and 100*/
template< >  class I< 100 >
  {
    void print(){ std::cout << " up to 100 and 100." << std::endl; }
  };
end example]


III. Solutions
Solutions within current standard
Using copy/paste
Already demonstrated in above examples. Lacks are obvious and well known.
By using macro replacement ( #define ).

[Example:
template< typename T >
 class A {
   void print(){ std::cout << "Primary template" << std::endl; }
  };

#define A_for_string(string_like_type)    \
    template< >    \
     class A< string_like_type T > {    \
       void print(){ std::cout << "String template" << std::endl; }    \
      }

A_for_string( char* );
A_for_string( std::string );
A_for_string( std::wsring );
end example] Replacing template with #define is bad idea. And again lacks are obvious and well known.
Involving the inheritance

[Example:
template< typename T >
 class A {
   void print(){ std::cout << "Primary template" << std::endl; }
  };

template< typename string_like_type >
 class A_string {
   void print(){ std::cout << "String template" << std::endl; }
  }

template<>
 class A< std::string >
  : public A_string< std::string > {
   // nothing
  };

template<>
 class A< std::wstring >
  : public A_string< std::wstring > {
   // nothing
  };
end example]
In this case there is additional class that need to bi hidden from user. This can be consider as code bloat.
Proposal
Use list of types in place on one type. Type list should be enclosed with '{' and '}'.
All examples can be easily extended in partial specialization examples.
float, string, ints:
Generalized primary template. Several specialized templates at once with list of types
[Example:
template< typename T > // Primary template. As per standard
 class A {
   void print(){ std::cout << "Primary template" << std::endl; }
  };

template<>
 class A< { char*, std::string, std::wstring } > { // Instead one type it is list of types.
   void print(){ std::cout << "String  specialization" << std::endl; }
  };

template<>
 class A< { float, double, long double } > {
   void print(){ std::cout << "Rational specialization" << std::endl; }
  };

template< typename T > 
 class A< std::vector < { T, short, int, long } > > { // Partial and and full specialization at once.
   void print(){ std::cout << "std::vector < Integers >" << std::endl; }
  };
end example]
enums:
Primary template with default parameter and two more types ( actually enums ).
Enums must appear in ascending order.
[Example:
enum colors{ red, green, blue, cyan, yellow, magenta, orange, pink };

// red is default, Other enums in list must appear in order of appearance in their definition.
template< enum Colors color = red { red, ... , blue } >
 class E {
    void print(){ std::cout << "Primary template with red default"<< std::endl;  }
  };

template< >
 class E< { cyan, ... , yellow } > { // Take all enums between cyan and key
  // Brand new specialization of print function
    void print(){ std::cout << "YELLOW"<< std::endl;  }
  };
end example]
Integer:
Demonstration of massive specialization on very small space.
Only integral types can use ellipsis.
Numbers must appear in ascending order.
[Example:
template< unsigned number = 0 >
 class I {
    void print(){ std::cout << "Primary template." << std::endl; }
  };

template< >
 class I< { 1, ..., 10 } >{ // Numbers must appear in increasing order
    void print(){ std::cout << " up to 10 and 10 ." << std::endl; }
  };

template< >
 class I< { 11, ..., 100 } > {
    void print(){ std::cout << " from 11 to 100 and 100." << std::endl; }
  };

template< >
 class I< 42 >{ // ERROR: Already specialized.
  {
    void print(){ std::cout << " 42." << std::endl; }
  };
end example]
Multi specialization on two ( or more ) parameters:
When two or more parameters are list of types ( or integers ) all pairs ( tuples ) will be specialized.
[Example:
template< typename S, typename T >
 class D {
    void print(){ std::cout << "Primary template." << std::endl; }
  };

template< >
 class D< { int, long }, { std::string, std::wstring } >{
    void print(){ std::cout << "{ int, long }, { std::string, std::wstring }" << std::endl; }
  };

template< >
 class D< int, std::wstring >{ // ERROR: Specialization already exists
    void print(){ std::cout << " int, std::wstring " << std::endl; }
  };
end example]

It is not possible to have some exception for some pair ( tuples ) of types.
[Example:
template< typename S, typename T >
 class E {
    void print(){ std::cout << "Primary template." << std::endl; }
  };

template< >
 class E< int, std::wstring >{ // OK. Specialization for int and std::wstring
    void print(){ std::cout << " int, std::wstring " << std::endl; }
  };
  
template< >
 class E< { int, long }, { std::string, std::wstring } >{ // ERROR: One pair is already specialized.
    void print(){ std::cout << "{ int, long }, { std::string, std::wstring }" << std::endl; }
  };

end example]

IV. Design Decisions
  • Massive (partial)specialization at the cost of one definition.
  • Due to fact the that same code are shared with other types it is not possible make different implementation mistakenly for some type of same group.
  • There is no need for additional code like in N3596 or similar solution. Which is one of the main goal i.e no additional code. One place and start place only.
  • Do not solve (and not intention): Reuse of existing (partial)specialization by modification, function/member excluding/adding/redefining.
V. Impact On the Standard
Core
No effect.
No new keywords.
No new tokens.
No changes to old syntax.
Pure extension.
Backward compatible.
Library
No effect.
No new class.
No new functions.
Existing code.
No effect.
signature.asc

Tony V E

unread,
Mar 20, 2018, 6:14:45 PM3/20/18
to Standard Proposals
On Tue, Mar 20, 2018 at 7:06 AM, Dejan Milosavljevic <dmi...@gmail.com> wrote:
> This language feature is called Concepts, and is already in the working draft for C++20

Concept allows you to make constraints on not (yet) exiting types.
This make specialization on existing types.


True, but do we need both? (Or all three, really).

We can currently specialize a single type.
With Concepts we can "specialize" a category of types.
With yours we can specialize a list of types.

Is it worth it?


Alberto Barbati

unread,
Mar 21, 2018, 3:55:39 AM3/21/18
to ISO C++ Standard - Future Proposals
Il giorno martedì 20 marzo 2018 12:06:41 UTC+1, Dejan Milosavljevic ha scritto:
> This language feature is called Concepts, and is already in the working draft for C++20

Concept allows you to make constraints on not (yet) exiting types.
This make specialization on existing types.

Concepts can do both things, as Barry clearly showed that they can specialize on existing types using the is_any_of_v technique. While your idea is interesting, we already have a much superior feature already voted in C++20, so I'm afraid we don't need yours.

A.

Dejan Milosavljevic

unread,
Mar 21, 2018, 4:55:54 AM3/21/18
to std-pr...@isocpp.org
No doubt that this problem can be solved within standard.
Document present tree solution within standard.
In here there are another two.

Main question what is the cost/effort of that?

For concepts there is need for concept which require pile of code to write/read/maintain.
Second example also add descent amount of code too and for the first read can be confusing for understanding.
Mainly we want to read code just once line by line, without scrolling up/down to see more code to understand our current focus.

This proposal shrink this to minimal.
One line to write. One line to read. One line to maintain.


--
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-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.

Alberto Barbati

unread,
Mar 21, 2018, 5:16:13 AM3/21/18
to ISO C++ Standard - Future Proposals
Il giorno mercoledì 21 marzo 2018 09:55:54 UTC+1, Dejan Milosavljevic ha scritto:
For concepts there is need for concept which require pile of code to write/read/maintain.

What? As long as the standard library includes a facility like is_any_of_v (and I'm sure that's going to happen), you don't need to write more than one extra line. I don't see a big difference between


template <typename T>
    requires is_any_of_v<T, char*, std::string, std::wstring>
class A<T> { ... };

and

template<>
class A< { char*, std::string, std::wstring } > { ... };

in terms of maintainability.
 
Second example also add descent amount of code too and for the first read can be confusing for understanding.

What second example? Please be more specific.
 
Mainly we want to read code just once line by line, without scrolling up/down to see more code to understand our current focus.

This proposal shrink this to minimal.
One line to write. One line to read. One line to maintain.

One more feature to learn, that is useful only in this specific case.

Richard Smith

unread,
Mar 21, 2018, 3:26:20 PM3/21/18
to std-pr...@isocpp.org
On 21 March 2018 at 02:16, Alberto Barbati <alberto...@gmail.com> wrote:
Il giorno mercoledì 21 marzo 2018 09:55:54 UTC+1, Dejan Milosavljevic ha scritto:
For concepts there is need for concept which require pile of code to write/read/maintain.

What? As long as the standard library includes a facility like is_any_of_v (and I'm sure that's going to happen), you don't need to write more than one extra line. I don't see a big difference between

template <typename T>
    requires is_any_of_v<T, char*, std::string, std::wstring>
class A<T> { ... };

and

template<>
class A< { char*, std::string, std::wstring } > { ... };

in terms of maintainability.

FWIW, the relevant concept is completely trivial.

  template<typename T, typename ...U> concept AnyOf = (std::is_same_v<T,U> || ...);

  template<AnyOf<char*, std::string, std::wstring> T>
  class A<T> { ... };

This appears to be strictly better than the A<{...}> approach, as it provides a natural way to give a name to the type (which you're almost certainly going to need inside the specialization).

Second example also add descent amount of code too and for the first read can be confusing for understanding.

What second example? Please be more specific.
 
Mainly we want to read code just once line by line, without scrolling up/down to see more code to understand our current focus.

This proposal shrink this to minimal.
One line to write. One line to read. One line to maintain.

One more feature to learn, that is useful only in this specific case.

--
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-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
Reply all
Reply to author
Forward
0 new messages