Check presence of custom overload of function when template overload is available - c++

I am designing an utility header that pumps binary data off an sf::InputStream. For ease of use, is comprises a single function name, readFromStream, that has a lot of (templated and non-templated) overloads for automatically deserializing standard-layout types and type compounds like vectors, tuples and my custom-designed grid class. The complete implementation can be found here: https://github.com/JoaoBaptMG/ReboundTheGame/blob/master/MainGame/utility/streamCommons.hpp
So, I have defined an overload readFromStream that pumps out a vector of any type by calling readFromStream again recursively:
template <typename T, typename std::enable_if<!is_optimization_viable<T>::value, int>::type = 0>
bool readFromStream(sf::InputStream &stream, std::vector<T> &value)
{
size_t size;
if (!readFromStream(stream, VarLength(size)))
return false;
std::vector<T> newVal(size, T());
for (auto &val : newVal)
if (!readFromStream(stream, val))
return false;
newVal.swap(value);
return true;
}
I'd like to write an optimized version for standard-layout classes for that there's not an overload for readFromStream, so we can exploit the memory layout of them and blit them in a single read call:
// trait is_optimization_viable is what I'm having trouble to write
template <typename T, typename std::enable_if<is_optimization_viable<T>::value, int>::type = 0>
bool readFromStream(sf::InputStream &stream, std::vector<T> &value)
{
size_t size;
if (!readFromStream(stream, VarLength(size)))
return false;
std::vector<T> newVal(size, T());
if (stream.read(newVal.data(), size*sizeof(T)) != size*sizeof(T))
return false;
newVal.swap(value);
return true;
}
Well, I could use a solution described on other answers to detect presence of a function, but there's a catch. When the type is standard-layout, I have a default readFromStream that reads like this:
template <typename T, typename std::enable_if<std::is_standard_layout<T>::value, int>::type = 0>
bool readFromStream(sf::InputStream &stream, T& value)
{
return stream.read((void*)&value, sizeof(T)) == sizeof(T);
}
So, there's always a function that does the serialization, not just the one I wanted. The problem I want to solve here is: how can I detect the presence of a non-default readFromString for type T, in order to disable the optimized version of readFromString for std::vector<T>?
I have tried to pull a few tricks. I can't limit the optimization to POD types because I'm using sf::Vector2<T> on some types I want to deserialize, which is not POD. I tried to compare the function addresses I get when I use a non-templatized and templatized function, like:
using FPtr = bool(*)(sf::InputStream&, T&);
return (FPtr)readFromStream == (FPtr)readFromStream<T>;
But, strangely enough, it didn't work. And I researched a lot of solutions, but none I could adapt to what I needed. Maybe it's not possible in C++, and I'll have to resort "marking" the types I don't want to be optimized. Or maybe it's some obscure template I haven't thought of. How could I do this?

As I understand it your problem is:
is_optimization_viable<T>;
could be defined by:
template<typename T>
using is_optimization_viable<T> = std::is_standard_layout<T>;
but for the fact that, for certain values of T that are standard layout
you nonetheless require a custom bool readFromStream(sf::InputStream &stream, T &value),
overload which means they are not optimization-viable.
Well as you must write these custom overloads, you know what those
exceptional values of T are. Say they are types X, Y, Z.
Then you can define the trait as:
#include <type_traits>
template<typename T, typename ...Us>
struct is_one_of;
template<typename T>
struct is_one_of<T> {
static constexpr bool value = false;
};
template<typename T, typename First, typename ...Rest>
struct is_one_of<T,First,Rest...> {
static constexpr bool value =
std::is_same<T,First>::value || is_one_of<T,Rest...>::value;
};
// ^ C++17: `std::disjunction` does the job
template<typename T>
using has_custom_read_from_stream = is_one_of<T,X,Y,Z>;
template<typename T>
struct is_optimization_viable {
static constexpr bool value = std::is_standard_layout<T>::value &&
!has_custom_read_from_stream<T>::value;
};
I appreciate that you'd rather avoid the ongoing maintenance of the
hard-coded type-list X, Y, Z, and prefer somehow to SFINAE-probe
whether a call readFromStream(s, t) will be a call to one of the
custom overloads for some std::declval-ed s and t.
But that's a mirage. You tell us, there will be some overload
readFromStream(s, t) that will compile whatever the type of t.
If so, a SFINAE probe will always tell you that Yes, readFromStream(s, t)
will compile - for any T as the unqualified type of t. And you
still have to make a compiletime decision as to whether T is one of
the custom types, and if not, whether it is standard-layout.
That's all there is to the problem. To tell whether T is one of
the custom types you must either test it for identity with any one
of them disjunctively, as shown, or your must find a trait independent of their
identities that is satisfied by all and only the custom types. As you
don't tell us what those custom types are, I can't suggest any such trait,
but if you find one then it will define or replace has_custom_read_from_stream<T>.
Incidentally, I second #NirFriedman's comment: is std::standard_layout really what you mean?

Related

Self referential c++20 concepts

What is the moral equivalent to the following invalid code?
// Suppose you want to check, as part of a concept,
// if some operation on a type results in a type that models such concept.
// you cannot constrain the resulting type with the same concept
// in the same way you can't have self referential types
// (that would result in recursive definitions)
template<class T>
concept Int = requires(const T& a, const T& b) {
{ a + b } -> Int; // compiler don't know what to do
};
Suppose you want to check, as part of a concept, if some operation on a type results in a type that models such concept.
That's infinite recursion. Like any functional recursion, you have to have a terminal condition. The normal way to define a terminal condition for template arguments is via a specialization. But concepts explicitly cannot be specialized, so there can be no terminal condition.
It's also logically incoherent, since you're trying to write a definition by using the thing you're trying to define. There is no "moral equivalent" to something that by definition doesn't make sense.
Your concept appears to be saying "T shall be a thing that I can add to another T and yield..." what? Do you want it to be able to yield some unrelated type U which can be added to another U to yield... again, what? Even ignoring that question, should U be able to be added to T? And if so, what should that yield?
When writing a concept, start with the use case, start by deciding what operations you want to perform.
It is possible to do such recursive template check, but it makes code difficult to read.
The principle is to forward recursive template check to a function found by dependent name look up, whose constraints will only be verified if the type does not already belong to a list of already checked types... If the type belong to the list of already checked type, the function is disabled by SFINAE, and an other function that does not recursively refers to the concept is selected by overload resolution:
See it in action: compiler-explorer-link
#include <type_traits>
namespace trying{
struct to_do{};
template <class...Checked, class T>
std::enable_if_t <(std::is_same_v <T,Checked> || ...), std::true_type>
too_complex(T &&, to_do);
template <class...Checked, class T>
std::false_type
too_complex(T &&,...);
}
template <class U, class T, class...Checked>
concept Integer_= requires(const T& a, const T& b, const U& to_be_readable)
{
requires decltype(too_complex <T, Checked...> (a + b, to_be_readable))::value ;
};
template <class T, class...Checked>
concept Integer = Integer_ <trying::to_do, T, Checked...>;
namespace trying{
template <class...Checked, class T>
requires (Integer <T, Checked...>)
std::enable_if_t <!(std::is_same_v <T,Checked> || ...), std::true_type>
too_complex(T &&, to_do);
}
struct x{
auto
operator + (x) const -> int;
};
struct y{
auto
operator + (y) const -> void*;
};
struct z2;
struct z1{
auto
operator + (z1) const -> z2;
};
struct z2{
auto
operator + (z2) const -> z1;
};
static_assert (Integer <int>);
static_assert (Integer <x>);
static_assert (!Integer <y>);
static_assert (Integer <z1>);
static_assert (Integer <z2>);
So yes it is possible... but I don't think it should be done.

What are the syntax and semantics of C++ templated code?

template<typename T, size_t M, size_t K, size_t N, typename std::enable_if_t<std::is_floating_point<T>::value, T> = 0>
void fastor2d(){//...}
I copied this line of code from cpp-reference(only the std::enable_if part, i do need T and all three of the size_t's), because i would like to use this function only when floating_types are used on it ... it does not compile.
Could somebody explain to me, why, and what it even does? While i am at it, how do you call this function afterwards?
Every tutorial or question here on SO gets bombed with answers, and that is great, but to someone who does not understand jacks*** of what is happening, even those are not really helpful.(sry, if possibly slightly agitated or aggressive)
EDIT: i greatly appreciate all answers as of now, i realize that my wording might have been a bit off ... i understand what a template parameter is, and know the difference between runtime and compiletime etc, but i just cant get a good grasp of the syntax behind std::enable_if
EDIT2:
template<typename T, size_t M, size_t K, size_t N, typename = std::enable_if_t<std::is_integral<T>::value>>
void fastor2d(){
Fastor::Tensor<T,M,K> A; A.randInt();
}
This is literally the only thing i need changed. Notice the random() part
template<typename T, size_t M, size_t K, size_t N, typename = std::enable_if_t<std::is_floating_point<T>::value>>
void fastor2d(){
Fastor::Tensor<T,M,K> A; A.random();
}
I'll try to explain this as simple as possible not to go into the language details too much since you asked for it.
Template arguments are compile time arguments (they do not change during the run-time of your application). Function arguments are run-time and have a memory address.
Calling this function would look something like this:
fastor2d<Object, 1, 2, 3>();
In the <> brackets you see the compile-time arguments or more accurately the template parameters, and the function in this case takes 0 runtime arguments in the () brackets. The last compile time argument has a default argument which is used to check whether the function should compile at all (enable_if type). If you want to know more clearly what enable if does you should search for the term SFINAE, which is a template metaprogramming technique used to determine whether a function or class should exist or not.
Here is a short SFINAE example:
template<typename T, typename = std::enable_if_t<std::is_floating_point<T>::value>>
void function(T arg)
{
}
function(0.3f); //OK
function(0.0); //OK double results in std::is_floating_point<double>::value == true
function("Hello"); //Does not exist (T is not floating point)
The reason the third function call fails, is because the function does not exist. This is because the enable if caused the function not to exist when the compile-time bool that is passed in as its' template argument is false.
std::is_floating_point<std::string>::value == false
Do note that a lot of people agree that the SFINAE syntax is horrible and that a lot of SFINAE code will not be necessary anymore with the introduction of concepts and constraints in C++ 20.
Rather than a top-down approach starting with you code snippet, I'll take a bottom-up approach to explain some important details about templates and what tools and techniques are involved.
At heart, templates are a tool that let you write C++ code that applies to a range of possible types, not strictly for a fixed type. In a statically-typed language, this is firstly a great tool for reusing code without sacrificing type safety, but in C++ in particular, templates are very powerful because they can be specialized.
Every template declaration begins with the keyword template, and a list of type or non-type (i.e value) parameters. Type parameters use the special keyword typename or class, and are used to let your code work over a range of types. Non-type parameters simply use the name of an existing type, and these let you apply your code to a range of values that are known at compile-time.
A very basic templated function might look like the following:
template<typename T> // declare a template accepting a single type T
void print(T t){ // print accepts a T and returns void
std::cout << t; // we can't know what this means until the point where T is known
}
This lets us reuse code safely for a range of possible types, and we can use it as follows:
int i = 3;
double d = 3.14159;
std::string s = "Hello, world!";
print<int>(i);
print<double>(d);
print<std::string>(s);
The compiler is even smart enough to deduce the template parameter T for each of these, so you can safely get away with the following, functionally identical code:
print(i);
print(d);
print(s);
But suppose you want print to behave differently for one type. Suppose, for example, you have a custom Point2D class that needs special handling. You can do this with a template specialization:
template<> // this begins a (full) template specialization
void print<Point2D>(Point2D p){ // we are specializing the existing template print with T=Point2D
std::cout << '(' << p.x << ',' << p.y << ')';
}
Now, anytime we use print with T=Point2D, the specialization is chosen. This is really useful, for example, if the generic template just doesn't make sense for one specific type.
std::string s = "hello";
Point2D p {0.5, 2.7};
print(s); // > hello
print(p); // > (0.5,2.7)
But what if we want to specialize a template for many types at once, based on a simple condition? This is where things become a little meta. First, let's try to express a condition in a way that lets them be used inside templates. This can be a little tricky because we need compile-time answers.
The condition here will be that T is a floating point number, which is true if T=float or T=double and false otherwise. This is actually fairly simple to achieve with template specialization alone.
// the default implementation of is_floating_point<T> has a static member that is always false
template<typename T>
struct is_floating_point {
static constexpr bool value = false;
};
// the specialization is_floating_point<float> has a static member that is always true
template<>
struct is_floating_point<float> {
static constexpr bool value = true;
};
// the specialization is_floating_point<double> has a static member that is always true
template<>
struct is_floating_point<double> {
static constexpr bool value = true;
}
Now, we can query any type to see if it's a floating point number:
is_floating_point<std::string>::value == false;
is_floating_point<int>::value == false;
is_floating_point<float>::value == true;
is_floating_point<double>::value == true;
But how can we use this compile-time condition inside another template? How can we tell the compiler which template to choose when there are many possible template specializations to choose from?
This is achieved by taking advantage of a C++ rule called SFINAE, which in basic English, says, "when there are many possible template specializations to choose from, and the current one doesn't make sense*, just skip it and try the next one."
There's a list of errors, when attempting to substitute template arguments into templated code, that cause the template to be ignored without an immediate compiler error. The list is a bit long and complex.
One possible way that a template doesn't make sense is if it tries to use a type that doesn't exist.
template<typename T>
void foo(typename T::nested_type x); // SFINAE error if T does not contain nested_type
This is the exact same trick that std::enable_if uses under the hood. enable_if is a template class accepting a type T and a bool condition, and it contains a nested type type equal to T only when the condition is true. This is also pretty easy to achieve:
template<bool condition, typename T>
struct enable_if {
// no nested type!
};
template<typename T> // partial specialization for condition=true but any T
struct enable_if<true, T> {
typedef T type; // only exists when condition=true
};
Now we have a helper that we can use in place of any type. If the condition we pass is true, then we can safely use the nested type. If the condition we pass is false, then the template is no longer considered.
template<typename T>
typename std::enable_if<std::is_floating_point<T>::value, void>::type // This is the return type!
numberFunction(T t){
std::cout << "T is a floating point";
}
template<typename T>
typename std::enable_if<!std::is_floating_point<T>::value, void>::type
numberFunction(T t){
std::cout << "T is not a floating point";
}
I completely agree that std::enable_if<std::is_floating_point<T>::value, void>::type is a messy way to spell out a type. You can read it as "void if T is floating point, and otherwise stop and try the next overload"
Finally, to take apart your example:
// we are declaring a template
template<
typename T, // that accepts some type T,
size_t M, // a size_t M,
size_t K, // a size_t K,
size_t N, // a size_t N,
// and an unnamed non-type that only makes sense when T is a floating point
typename std::enable_if_t<std::is_floating_point<T>::value, T> = 0
>
void fastor2d(){//...}
Note the = 0 at the end. That's simply a default value for the final template parameter, and it lets you get away with specifying T, M, K, and N but not the fifth parameter. The enable_if used here means that you can provide other templates called fastor2d, with their own sets of conditions.
First of all, I'll rewrite your function in a working form
template <typename T, size_t M, size_t K, size_t N,
std::enable_if_t<std::is_floating_point<T>::value, int> = 0>
void fastor2d() // ..........................................^^^ int, not T
{ }
The point is that I've changed the second template argument of std::enable_if_t form T to int.
I've also removed the typename before std::enable_if_t but isn't important: the typename is implicit in the _t at the end of std::enable_if_t, introduced from C++14. In C++11 the correct form is
// C++11 version
typename std::enable_if<std::is_floating_point<T>::value, int>::type = 0
// ^^^^^^^^ no _t ^^^^^^
But why it works?
Start from the name: SFINAE.
Is a short form for "Substitution Failure Is Not An Error".
It's a C++ rule so that when you write some thing as
template <int I, std::enable_if_t< I == 3, int> = 0>
void foo ()
{ }
and I is 3, the condition of std::enable_if_t is true so std::enable_if_t< I == 3, int> is substituted with int so foo() is enabled but when I isn't 3, the condition of std::enable_if_t if false so std::enable_if_t< I == 3, int> is not substituted so foo() isn't enabled but this ins't an error (if, through overloading, there is another foo() function, enabled, that matches the call, obviously).
So where is the problem in your code?
The problem is that std::enable_if_t is substituted, when the first template parameter is true, with the second parameter.
So if you write
std::enable_if_t<std::is_floating_point<T>::value, T> = 0
and you call
fastor2d<float, 0u, 1u, 2u>();
the std::is_floating_point<float>::value (but you can also use the shorter form std::is_floating_point_v<T> (_v and not ::value)) so the substitution take place and you get
float = 0
but, unfortunately, a template value (not type) parameter can't be of type floating point, so you get an error.
If you use int instead of T, the substitution give you
int = 0
and this is correct.
Another solution can be use the following form
typename = std::enable_if_t<std::is_floating_point<T>::value, T>
as suggested by Andreas Loanjoe, because the substitution give you
typename = float
that is a valid syntax.
But this solution has the drawback that doesn't works when you want to write two alternative functions, as in the following example
// the following solution doesn't works
template <typename T,
typename = std::enable_if_t<true == std::is_floating_point<T>::value, int>>
void foo ()
{ }
template <typename T,
typename = std::enable_if_t<false == std::is_floating_point<T>::value, int>>
void foo ()
{ }
where works the solution based on the value
// the following works
template <typename T,
std::enable_if_t<true == std::is_floating_point<T>::value, int> = 0>
void foo ()
{ }
template <typename T,
std::enable_if_t<false == std::is_floating_point<T>::value, int> = 0>
void foo ()
{ }

Why do `SFINAE` (std::enable_if) uses bool literals instead of `true_t` / `false_t` tag classes?

I am trying to learn about SFINAE (i am following this tutorial), but there are some... "design choices" I do not understand and, as such, I find them confusing.
Let's assume I have a situation like this (included re-implementation of std::enable_if is there just to demonstrate how I understand enable_if)
// A default class (class type) declaration. Nothing unusual.
template <bool, typename T = void>
struct enable_if
{};
// A specialisation for <true, T> case. I understand 'why-s' of this.
// -- 'why-s': if I attempt to access 'enable_if<false, T>::type' (which does not exist) I will get a substitution failure and compiler will just "move-on" trying to match "other cases".
template <typename T>
struct enable_if<true, T> {
typedef T type;
};
// Here lies my problem:
template <class T,
typename std::enable_if<std::is_integral<T>::value,T>::type* = nullptr>
void do_stuff(T& t) { /* do stuff */ };
(1) The very 1st thing I have a "problem" with, is bool literal (true/false). I understand they are correct and templates can accept compile-time constant values of primitive data types (plain-old-data types) but if I were tasked to design the enable_if "mechanisms" instead of using true/false I would create a tag classes true_t(or True) and false_t (or False) as follows:
class true_t {}; // or True
class false_t {}; // or False
template<typename T>
class is_integral // just to have "something" to use with "enable_if"
{
using result = false_t;
};
template<>
class is_integral<int32_t> // same with all other int types
{
using result = true_t;
};
template <typename B, typename T = void>
struct enable_if
{};
template <typename T>
struct enable_if<true_t, T>
{
using type = T;
};
(2) The second thing I find redundant is the need to specify typename T template parameter. Wouldn't it be easier / better to just implement enable_if as follows:
template <typename B>
struct enable_if
{};
template <>
struct enable_if<true_t>
{
using type = void; // the 'type' exists therefore substitution failure will not occur.
};
I am well aware that all my propositions are extremely inferior to the currently existing solutions, but I don't understand why... What portion of the functionality (important functionality) of current SFINAE did i shave off? (Not even realizing it...)
I know that, on this site, I am obligated to ask a single question within a... single "question-post-like" format, but if you find it acceptable could I also ask what will this syntax:
std::enable_if</* ... */>::type* = nullptr
accomplish? It's beyond my understanding right now...
The very 1st thing I have a "problem" with, is bool literal (true/false). I understand they are correct and templates can accept compile-time constant values of primitive data types (plain-old-data types) but if I were tasked to design the enable_if "mechanisms" instead of using true/false I would create a tag classes true_t(or True) and false_t (or False) as follows
The issue with use a tag type instead of just a bool is that you have to add extra complexity to the code. If you want to check a compile time condition, like sizeof for instance, you couldn't just do sizeof(T) == 8. You would have to make an abstraction that does the check and the returns the appropriate tag type.
The second thing I find redundant is the need to specify typename T template parameter. Wouldn't it be easier / better to just implement enable_if as follows
Not really. What if you want to use the SFINAE for the return type? You would only be able to have a void function then, which is unnecessarily limiting. Instead what you can do is use what was later added in C++14 and C++17 and make aliases. This makes the names non dependent and lets you drop the typename
template< bool B, class T = void >
using enable_if_t = typename enable_if<B,T>::type;
template< class T >
inline constexpr bool is_integral_v = is_integral<T>::value;
This allows you to rewrite
template <class T,
typename std::enable_if<std::is_integral<T>::value,T>::type* = nullptr>
void do_stuff(T& t) { /* do stuff */ };
to
template <class T,
std::enable_if_t<std::is_integral_v<T>,T>* = nullptr>
void do_stuff(T& t) { /* do stuff */ };
although I prefer to use a bool for the type of enable_if_t like
template <class T,
std::enable_if_t<std::is_integral_v<T>, bool> = true>
void do_stuff(T& t) { /* do stuff */ };
I know that, on this site, I am obligated to ask a single question within a... single "question-post-like" format, but if you find it acceptable could I also ask what will this syntax:
std::enable_if</* ... */>::type* = nullptr
accomplish?
It makes a pointer to the type that std::enable_if "returns" and sets it to null pointer. The goal here is to make a template parameter that will only exist if the condition is true. You could rewrite it to
typename = typename std::enable_if</* ... */>::type
so instead of having a non type parameter you have a type parameter. They both accomplish the same thing but the latter wont work with overloading the function for different enable_if's since default template parameters are not part of the signature. The first version which uses non type parameters is included in the function signature and does allow you to overload the enable_if's.
First off there exists tag-types for true and false, namely std::true_type and std::false_type.
Let's say we made the enable_if work with this instead of a bool parameter. You could then no longer do things like std::enable_if<1 == 1>::type since 1 == 1 evaluates to a bool. So does most things you will want to test here.
On the other hand, the existing tag types can be used in a enable_if since they contain a value and have a operator() that return said value.
So it seems to me that a lot of convenience would be lost in doing it your way, and from what I can see nothing would be gained.
For point 2, it's simply a convenience to be able to specify what type you want enable_if to hold if it's true. It defaults to void, but if you want you can easily have it deduce an int, double ect. which can be useful sometimes.

Represent a set of types using C++ template meta-programming

How to write a template meta_set<class...Args> such that meta_set::type` is same for all permutations of Ts?
In other words, we want to have the same meta_set<>::type whenever the list of arguments is the same regardless of the order, that is, when viewed as a set (or multiset if it's easier).
For example,
std::is_same< meta_set<int,double,string>::type, meta_set<double,string,int>::type >::value == true
std::is_same< meta_set<int,double,string>::type, meta_set<double,string,bool>::type >::value == false
This may come handy in situations when you want to have a single instantiation of a template per a set of template parameters.
NOTE: this is not a homework assignment, but something I got curious about when working on a template heavy code at work. I'm not a meta-programming expert, so I thought maybe people can share their knowledge.
There is no way to globally order all types at compile time; access to things like typeid(T).before(typeid(U)) is not constexpr. So you cannot make two arbitrary meta_set<A,B> and meta_set<B,A> be the same type, as you cannot sort.
There is no way to modify the behavior of std::is_same to return true if the types that are not the same. Any code that attempted to do so (say via specializing std::is_same) would violate the requirements of std::is_same, which would make the program ill-formed, no diagnostic required.
If you restricted your set of types to some subset of all types, you can do this. The easiest way would be to have a centralized list:
template<class...T>
struct types_t {};
using global_order = types_t<int, double, std::string>;
then, with some template metaprogramming, you can get the index of types in the global_order, and then write a type-sorter based on this.
Then
template<class Types>
struct type_sorter;
template<class...Ts>
struct type_sorter<types_t<Ts...>> {
// todo
using type=types_t< result_somehow >;
};
Once that is written:
template<class...Ts>
using meta_set = typename type_sorter< types_t<Ts...> >::type;
would work.
There are probably solutions on how to sort stuff at compile time with templates on stack overflow. Personally I find merge sort easiest to write in template metaprogramming of all the n log(n) sorts. Last time it did it it took about 100-odd lines of dense template code? Including writing a TMP library. However, TMP libraries now exist, and may even have type-sorting code pre-written for you.
Now, a single global ordering is the easiest thing to do. We could make it a bit more powerful by teaching the type sorter about templates, ordering templates, and then ordering said template instances by their constituant types and values, etc.
That gets hard, and it requires work for each and every template, kind of template (so template<class...>class vs template<class, std::size_t>class) and base type (int, float, struct foo) supported.
It would be a lot of work.
Slightly easier would be to write a custom trait
template<class Lhs, class Rhs>
struct smart_is_same;
that would act like std::is_same, except when fed meta_sets would check if they have the same contents instead of looking for strict type equality. However, your comments mention that this isn't your actual problem, but rather you are talking about passing the meta_set arguments to a different template and wants them in a canonical order.
The only conceivable way to canonically represent a set of types is with a sorted list (or something that can be converted to a sorted list, e.g. a binary search tree). But there's no natural compile-time ordering between types in C++, so there's nothing to sort by.
Should C++ require std::type_info::before to be constexpr, or define a template with similar functionality, say std::is_before<typename A, typename B>, it would create a global static ordering between types which would make creating canonically ordered lists possible. Sadly this is not the case.
Lacking compiler-supported ordering, the programmer must define their own is_before<A,B> for every possible pair of types, which is of course not possible for every pair of types, but only for some finite known-in-advance set.
My idea is not to sort the types, but just to check that a type in first meta set appears in the second.
template <typename... Args>
struct TypeList;
template <typename Head, typename... Tail>
struct TypeList<Head, Tail...>
{
using TailList = TypeList<Tail...>;
using HeadType = Head;
static_assert(!TailList::template contains<Head>(), "Types must be unique");
static constexpr int size()
{
return 1 + TailList::size();
}
template <typename Type>
static constexpr bool contains()
{
return std::is_same<Head, Type>::value || TailList::template contains<Type>();
}
};
template<>
struct TypeList<>
{
static constexpr int size()
{
return 0;
}
template <typename Type>
static constexpr bool contains()
{
return false;
}
};
template <typename ListLhs, typename ListRhs>
struct IsSame
{
static constexpr bool value()
{
return ListLhs::size() == ListRhs::size() && valueImpl();
}
static constexpr bool valueImpl()
{
return ListLhs::template contains<typename ListRhs::HeadType>() &&
IsSame<ListLhs,typename ListRhs::TailList>::valueImpl();
}
};
template <typename ListLhs>
struct IsSame<ListLhs, TypeList<>>
{
static constexpr bool value()
{
return false;
}
static constexpr bool valueImpl()
{
return true;
}
};
template <>
struct IsSame<TypeList<>, TypeList<>>
{
static constexpr bool value()
{
return true;
}
static constexpr bool valueImpl()
{
return false;
}
};
struct MyStruct{};
using Types = TypeList<int, bool, char, MyStruct, double>;
using TypesSame = TypeList<int, MyStruct, bool, char, double>;
using LessTypes = TypeList<int, bool, char>;
using EmptyTypes = TypeList<>;
static_assert(IsSame<Types, TypesSame>::value(), "Underlying types should be the same");
static_assert(!IsSame<Types, LessTypes>::value(), "Less types");
static_assert(!IsSame<Types, EmptyTypes>::value(), "Not the same as Empty");
static_assert(IsSame<EmptyTypes, EmptyTypes>::value(), "Empty types");

SFINAE technique for free function wrappers

I would really like to be able to have a free function that adapts to whatever types its being given.
e.g.
template <typename T> bool ReadLine(T & reader, std::string & line)
{
return reader.ReadString(line);
}
For some T, the correct function is reader.ReadString(buffer). But for others, it should be reader.ReadLine(buffer). And of course, there may be other patterns in the future. The point being to adapt the free function ReadLine(from, into) to work with any reasonable set of from & into (I've forced the destination buffer to be a std::string to simplify things here).
Now, I can make a non-template version of ReadLine for any concrete type I want, but what I really need is the ability to partially specialize for classes of types, such that those which support the pattern reader.ReadString() all end up using that, and those which support reader.ReadLine() use that, and in the future I can add other patterns without disturbing anything that already works.
I'm aware that I can create a policy class, such as LineReaderPolicy, which knows for a given T which pattern to use (it would have to be partially specialized depending on T to map it to the correct pattern.
But is there a better, more C++14 way of solving this?
This is one of those "God it seems that templates are really, really close to being really, really useful... but for this constantly recurring problem..."
Compostability is better than ever with C++11/14, but it still seems that this fundamental problem is unsolved? Or is it?!
How would you suggest I write a set of free functions that adapt to any reasonable T to read a line from it? Whether T be a string stream, an output iterator, a file handle, a string, a string-view, etc., etc...
I just cannot think that C++ is really come of age until I can write a reasonable
template <typename T> size_t length(T t) { return t.size(); }
For which I can then extend that into any reasonable T, and stop having to write code that know so many details of T, but can inter-operate with tons of Ts via such flexible free function adapters...
If you can ensure that at most one of reader.ReadString or reader.ReadLine is defined, use SFINAE to control overloading (Live at Coliru):
template <typename T>
auto ReadLine(T& reader, std::string& line) -> decltype(reader.ReadString(line)) {
return reader.ReadString(line);
}
template <typename T>
auto ReadLine(T& reader, std::string& line) -> decltype(reader.ReadLine(line)) {
return reader.ReadLine(line);
}
The inapplicable implementation will trigger SFINAE and be removed from the overload set, leaving only the correct implementation.
In C++14 you'll be able to omit the trailing return type and simply use return type deduction.
In a future revision of C++, Concepts Lite will enable this to be done more cleanly. Given concepts that discriminate the two different kinds of readers - say StringReader and LineReader:
template <typename T>
concept bool StringReader() {
return requires(T& reader, std::string& line) {
{reader.ReadString(line)} -> bool;
};
}
template <typename T>
concept bool LineReader() {
return requires(T& reader, std::string& line) {
{reader.ReadLine(line)} -> bool;
};
}
you'll be able to directly constrain your implementations to the set of types that model those concepts:
bool ReadLine(StringReader& reader, std::string& line) {
return reader.ReadString(line);
}
bool ReadLine(LineReader& reader, std::string& line) {
return reader.ReadLine(line);
}
Hopefully you'll be able to reuse those concepts elsewhere to justify the "new-special-better" syntax being substantially longer than the old nasty syntax. Concepts Lite also will make it possible to handle types that model both concepts by explicit disambiguation:
template <typename T>
requires StringReader<T>() && LineReader<T>()
bool ReadLine(T& reader, std::string& line) {
// Call one, or the other, or do something completely different.
}
I just cannot think that C++ is really come of age until I can write a reasonable
template <typename T> size_t length(T t) { return t.size(); }
For which I can then extend that into any reasonable T, and stop having to write code that know so many details of T, but can inter-operate with tons of Ts via such flexible free function adapters...
You want Concepts Lite, which I hope will arrive in C++17:
template<typename T>
concept bool Has_size()
{
return requires(T t) {
t.size() -> Integral;
};
}
template<typename T>
concept bool Has_length()
{
return requires(T t) {
t.length() -> Integral;
};
}
template <Has_size T> auto length(T t) { return t.size(); }
template <Has_length T> auto length(T t) { return t.length(); }
Until then you can use SFINAE to emulate it, which can be done in many ways, the tersest for your example is probably just a trailing-return-type as shown in Casey's answer.
template <typename T>
auto length(T t) -> decltype(t.size()) { return t.size(); }
template <typename T>
auto length(T t) -> decltype(t.length()) { return t.length(); }
template<typename>struct type_sink{typedef void type;};
template<typename T>using TypeSink=typename type_sink<T>::type;
template<typename T,typename=void>
struct has_x:std::false_type{};
template<typename T>
struct has_x<T,TypeSink(decltype(std::declval<T>().x())>:std::true_type{};
is a pretty simple traits class for 'does a type have a .x() method, and can be generalized.
We can then use tag dispatching to direct our function to custom implementations.
template<typename T>
bool do_x_helper(T&t, std::true_type ){
return t.x();
}
template<typename T>
bool do_x_helper(T7, std::false_type ){
// alternative
}
template<typename T>
bool do_x(T& t){
return do_x_helper( t, has_x<T>() );
}
This technique lets you have complex tests and method bodies. You basically have to do overload resolution and dispatching manually, but it gives you full control. It is similar to the techniques that std algorithms use to dispatch iterator types: in that case more than just true_type and false_type is dispatched on.