Dynamic convertibility type trait - c++

Is it possible to enumerate a list of types to which a type can be converted to? Or would this require a technique similar to static reflection?
struct a {
operator int() const
{ return i; }
explicit operator float() const
{ return f; }
int i;
float f;
};
// enumerated type list for type 'a': tuple<int, float>
One of the things I would like to be able to do—which is related to this but with a somewhat narrower scope—is to check if a type is convertible to, let's say, an integral type, without having to explicitly list them.
The only way I'm able to do this now, is to create a tuple-like type-list of all those integer types, throw it in std::is_convertible and expand them within a std::conjunction. But I would really prefer to use a way which doesn't require me write out all the types of a specific type class. I'm specifically looking for a solution that is compatible with C++17 but if that is not possible or is simply too cumbersome, a C++20 solution is acceptable too.
The first part of my question seems to be well answered by Igor Tandetnik and Barry. Enumerating all of the types to which a type can be converted to would be undesirable for the reasons they have laid out. Enumerating the non-templated user-defined converion operators of a type could be useful but that would require someting like static reflection, which isn't possible for the time being.
Regarding the second part of my question, it seems that there are some nuances involved with type conversions between arithmetic types. Since they are all convertible to each other, finding the type trait that fits my needs maybe requires a different approach. I've decided to list a couple examples to better describe the requirements of this type trait.
#include <type_traits>
#include <utility>
struct a {
operator int() const
{ return i; }
explicit operator float() const
{ return f; }
int i;
float f;
};
// this succeeds, but instead of explicitly writing out 'int' I would like to
// express my intent and write someting as 'any_integral_type' (note: I
// understand that integral types might be too narrow to properly detect,
// so 'any_arithmetic_type' could be acceptable too) and I would
// like to do so without listing all of the integral types by hand
static_assert(std::is_convertible_v<a, int>);
struct b {
int i;
float f;
};
// the difference between type 'a' and type 'b' seems rather obvious and
// I would like to have a type trait that can express that. how to write
// 'assert that b cannot be converted to any type that belongs to the
// std::integral_types' without explicitly writing 'int'?
static_assert(not std::is_convertible_v<b, int>);
// another idea is to use the unary addition operator to force the implicit
// conversion, this way we don't have to be upfront about which type to convert
// to, but this runs into ambiguity issues when there is more than one viable
// conversion operator (e.g.: when operator float isn't marked explicit)
static_assert(std::is_integral_v<decltype(+std::declval<a>())>);
Live example.

Is it possible to enumerate a list of types to which a type can be converted to?
No. Such a list of types is infinite. For instance, your a is convertible to int and float, yes. But also short and double and char and so forth, as far as obvious things go.
But then also std::any because it's copyable. And std::optional<a>. And std::variant<a>. And then std::variant<a, T> for all types T that are not a or a const (even if a is convertible to T, like int). Which is an obviously infinite list, even by itself. And then std::variant<a, T1, T2>, etc.
So not only is such a list of types infinite, but it's uncountably infinite.
Or would this require a technique similar to static reflection?
I suspect what you actually are asking for is a very narrow question: Given a type T, what are all of its conversion functions? For those conversion functions that are not functinon templates (you can't really enumerate template <typename T> operator T() const; for instance), then yes -- static reflection would let you enumerate that list.
But note that that list is just going to be the list of types that T has conversion functions into. That list is not the list of types that T is convertible to. Just a subset thereof.

Related

ComputeLibrary data type templates

In the ARM ComputeLibrary, we can have a Tensor object of various types. When choosing the type of Tensors, we pass the type to the initaliser of the Tensor's allocator, such as float32 here:
my_tensor.allocator()->init(armcl::TensorInfo(shape_my_tensor, 1, armcl::DataType::F32));
A better introduction to the topic of tensor allocation can be found here.
There are several ARMCL types to choose from (see here for a list). Notice that the ComputeLibrary types are not primitive ones, though one can easily copy primitive typed data to them.
However, when writing templated C++ code, where one can have functions defined for arbitrary types, this "type choice which is not a type" creates a design problem.
Say I want to write a function that takes data of primitive types such as int, float, or double. In the templated function, this type would be referred to as being of type T (or whatever).
Now say I want to copy this data to an ARMCL Tensor within the scope of the templated function. This tensor needs to be initalised to the correct datatype. We need this datatype to be a good fit for the type T, so if T is a float, then our ARMCL should be of type F32, if T is an int then our tensor should be S8, etc.
We need some sort of mapping between primitive types, and the ARMCL types.
Would a "nice" and sensible approach be to have a utility function that takes the type T and maybe using a switch statement, and something like std::is_same. The switch statement would then return the appropriate ARM Compute Library DataType object for T? Or is there a different approach that might be more elegant?
I have been looking around the docs to see if this is already handled, but to no avail yet. If it isn't handled, then perhaps this is not a specific to ARMCL question, and is broader in scope.
Well... armcl types are, if I understand correctly, enum values.
So a possible approach is a template struct, with full specializations, with a value in it.
I mean... something as
template <typename>
struct typeMap;
template <>
struct typeMap<int>
{ static constexpr auto value = armcl::DataType::S8; };
template <>
struct typeMap<float>
{ static constexpr auto value = armcl::DataType::F32; };
// other cases
You can use it as follows
template <typename T>
void foo ()
{ bar(typeMap<T>::value); }

call of template member function

If I have a class
class A {
template<class T>
void foo(T t);
};
what is the difference / the way of the book to call foo?
a.foo(t)
or
a.foo<X>(t)
?
a.foo(t) will basically deduce T to the type of t. If this is fine with you, use this.
a.foo<X> will force T to be X and the correctness of the expression now depends on if t is convertible to X. Sometimes you desire this, if you want t to be converted to X, but you could always write this as a.foo(X{t}). The second form is not entirely equivalent, since it also allows explicit conversions.
In the second variant, X may not be the type of t. E.g., if t is a float, but you say a.foo<int>(t), no floating variant will be deduced, but rather the integer-variant (if not exists already), and a float-to-int conversion will happen.
If the types are identical, there is no difference. However, one never knows for the whole lifetime of the code whether the types never change.

C++ Types Impossible to Name

While reading Wikipedia's page on decltype, I was curious about the statement,
Its [decltype's] primary intended use is in generic
programming, where it is often
difficult, or even impossible, to name
types that depend on template
parameters.
While I can understand the difficulty part of that statement, what is an example where there is a need to name a type that cannot be named under C++03?
EDIT: My point is that since everything in C++ has a declaration of types. Why would there ever be a case where it is impossible to name a type? Furthermore, aren't trait classes designed to yield type informations? Could trait classes be an alternative to decltype?
The wikipedia page you link has a perfect example:
int& foo(int& i);
float foo(float& f);
template <class T> auto transparent_forwarder(T& t) −> decltype(foo(t)) {
return foo(t);
}
Note that foo(int&) returns int& (a reference type) while foo(float&) returns float (a nonreference type). Without decltype, it's impossible within the template to specify a type which represents "the return type of the function foo which takes an argument t of type T".
In this example, it's not a particular concrete type which is impossible to express -- either int& or float are individually expressible -- but a higher level generic class of types.
EDIT: and to answer your comment to another answer, this example is inexpressible in C++03. You cannot have a function template which will wrap any function T1 foo(T2) and match both argument and return type of the wrapped function.
There are types in C++0x (and in C++03, but less often) that cannot be named explicitly, such as the type decltype(f) after the declaration auto f = [](int x) -> int {return x;};. You would need to typedef that decltype result to something to get a name at all. Traits classes can be used for determining return types, but they are messy, and the user needs to duplicate all of their function overloads with traits class overloads; that is difficult to do correctly for cases such as functions applying (through implicit conversion of pointers) to all subclasses of a given base class.
As you pointed out, the type if it exist is known by the compiler, otherwise it wouldn't exist. However, it is not always readily or even accessible to the programmer in C++03.
N1607 mention the following in its conclusion:
In C++2003, it is not possible to
express the return type of a function
template in all cases. Furthermore,
expressions involving calls to
function templates commonly have very
complicated types, which are
practically impossible to write by
hand
The question is how do we access this type as a programmer. This is not always a trivial process, often impracticable. It is increasingly complex when you have an expression for which you desire to know the result type. You would have to break it into pieces in order to figure the result types. It is not possible to simplify this process using templates (not without evaluating the expression anyhow). Breaking the expression will be error-prone, tedious and a nightmare to maintain. Think of this code:
x.g()[b.a(e)]->f();
With C++98/TR1, it is often infeasible to name types that depend on template parameters. Traits offers us so much information, but eventually decltype is a much cleaner solution to many problems. A lot of the information available to you when meta programming is only available because libraries, such as boost or loki, use several tricks hidden in the dark corners of the C++98 language.
Of course this is irrelevant to your question but I believe that it is worthy to mention that C++98 compilers already have mechanics to know these types. This is exactly what sizeof offers, except that it returns you a size. decltype reuse some of this functionality and solves these problems with greater elegance.
As for a different (academic) example:
struct Foo
{
struct
{
int x;
} bar;
};
template<typename T>
void
f(const T& t)
{
// C++03, How can I name the type of T::bar ?
// C++0x
// decltype(t.bar) cpy;
// Do stuff with our local cpy
}
int
main()
{
f(Foo());
}

C++: type_info to distinguish types

I know that compilers have much freedom in implementing std::type_info functions' behavior.
I'm thinking about using it to compare object types, so I'd like to be sure that:
std::type_info::name must return two different strings for two different types.
std::type_info::before must say that Type1 is before Type2 exclusive-or Type2 is before Type1.
// like this:
typeid(T1).before( typeid(T2) ) != typeid(T2).before( typeid(T1) )
Two different specialization of the same template class are considered different types.
Two different typedef-initions of the same type are the same type.
And finally:
Since std::type_info is not copyable, how could I store type_infos somewhere (eg: in a std::map)? The only way it to have a std::type_info always allocated somewhere (eg: on the stack or on a static/global variable) and use a pointer to it?
How fast are operator==, operator!= and before on most common compilers? I guess they should only compare a value. And how fast is typeid?
I've got a class A with a virtual bool operator==( const A& ) const. Since A has got many subclasses (some of which are unknown at compile time), I'd overload that virtual operator in any subclass B this way:
virtual bool operator==( const A &other ) const {
if( typeid(*this) != typeid(other) ) return false;
// bool B::operator==( const B &other ) const // is defined for any class B
return operator==( static_cast<B&>( other ) );
}
Is this an acceptable (and standard) way to implement such operator?
After a quick look at the documentation, I would say that :
std::type_info::name always returns two different strings for two different types, otherwise it means that the compiler lost itself while resolving types and you shouldn't use it anymore.
Reference tells : "before returns true if the type precedes the type of rhs in the collation order. The collation order is just an internal order kept by a particular implementation and is not necessarily related to inheritance relations or declaring order."
You therefore have the guarantee that no types has the same rank in the collation order.
Each instantiation of a template class is a different type. Specialization make no exceptions.
I don't really understand what you mean. If you mean something like having typedef foo bar; in two separate compilation units and that bar is the same in both, it works that way. If you mean typedef foo bar; typedef int bar;, it doesn't work (except if foo is int).
About your other questions :
You should store references to std::type_info, of wrap it somehow.
Absolutely no idea about performance, I assume that comparison operators have constant time despite of the type complexity. Before must have linear complexity depending on the number of different types used in your code.
This is really strange imho. You should overload your operator== instead of make it virtual and override it.
Standard 18.5.1 (Class type_info) :
The class type_info describes type
information generated by the
implementation. Objects of this class
effectively store a pointer to a name
for the type, and an encoded value
suitable for comparing two types for
equality or collating order. The
names, encoding rule, and collating
sequence for types are all unspecified
and may differ between programs.
From my understanding :
You don't have this guarantee regarding std:type_info::name. The standard only states that name returns an implementation-defined NTBS, and I believe a conforming implementation could very well return the same string for every type.
I don't know, and the standard isn't clear on this point, so I wouldn't rely on such behavior.
That one should be a definite 'Yes' for me
That one should be a definite 'Yes' for me
Regarding the second set of questions :
No, you cannot store a type_info. Andrei Alexandrescu proposes a TypeInfo wrapper in its Modern C++ Design book. Note that the objects returned by typeid have static storage so you can safely store pointers without worrying about object lifetime
I believe you can assume that type_info comparison are extremely efficient (there really isn't much to compare).
You can store it like this.
class my_type_info
{
public:
my_type_info(const std::type_info& info) : info_(&info){}
std::type_info get() const { return *info_;}
private:
const std::type_info* info_;
};
EDIT:
C++ standard 5.2.8.
The result of a
typeid expression is an lvalue of
static type const std::type_info...
Which means you can use it like this.
my_type_info(typeid(my_type));
The typeid function returns an lvalue (it is not temporary) and therefore the address of the returned type_info is always valid.
The current answers for questions 1 and 2 are perfectly correct, and they're essentially just details for the type_info class - no point in repeating those answers.
For questions 3 and 4, it's important to understand what precisely is a type in C++, and how they relate to names. For starters, there are a whole bunch of predefined types, and those have names: int, float, double. Next, there are some constructed types that do not have names of their own: const int, int*, const int*, int* const. There are function types int (int) and function pointer types int (*)(int).
It's sometimes useful to give a name to an unnamed type, which is possible using typedef. For instance, typedef int* pint or typedef int (*pf)(int);. This introduces a name, not a new type.
Next are user-defined types: structs, classes, unions. There's a good convention to give them names, but it's not mandatory. Don't add such a name with typedef, you can do so directly: struct Foo { }; instead of typedef struct {} Foo;. It's common to have class definitions in headers, which end up\ in multiple translation units. That does mean the class is defined more than once. This is still the same type, and therefore you aren't allowed to play tricks with macros to change the class member definitions.
A template class is not a type, it's a recipe for types. Two instantiations of a single class template are distinct types if the template arguments are different types (or values). This works recursively: Given template <typename T> struct Foo{};, Foo<Foo<int> > is the same type as Foo<Foo<Bar> > if and only if Bar is another name for the type int.
Type_info is implementation defined so I really wouldn't rely on it. However, based on my experiences using g++ and MSVC, assumptions 1,3 and 4 hold... not really sure about #2.
Is there any reason you can't use another method like this?
template<typename T, typename U>
struct is_same { static bool const result = false; };
template<typename T>
struct is_same<T, T> { static bool const result = true; };
template<typename S, typename T>
bool IsSame(const S& s, const T& t) { return is_same<S,T>::result; }
Since std::type_info is not copyable, how could I store type_infos somewhere (eg: in a std::map)? The only way it to have a std::type_info always allocated somewhere (eg: on the stack or on a static/global variable) and use a pointer to it?
This is why std::type_index exists -- it's a wrapper around a type_info & that is copyable and compares (and hashes) by using the underlying type_info operations

What is the difference between static_cast and Implicit_cast?

What is implicit_cast? when should I prefer implicit_cast rather than static_cast?
I'm copying over from a comment i made to answer this comment at another place.
You can down-cast with static_cast. Not so with implicit_cast. static_cast basically allows you to do any implicit conversion, and in addition the reverse of any implicit conversion (up to some limits. you can't downcast if there is a virtual base-class involved). But implicit_cast will only accept implicit conversions. no down-cast, no void*->T*, no U->T if T has only explicit constructors for U.
Note that it's important to note the difference between a cast and a conversion. In the following no cast is going on
int a = 3.4;
But an implicit conversion happens from double to int. Things like an "implicit cast" don't exist, since a cast is always an explicit conversion request. The name construct for boost::implicit_cast is a lovely combination of "cast using implicit conversions". Now the whole implementation of boost::implicit_cast is this (explained here):
template<typename T> struct identity { typedef T type; };
template<typename Dst> Dst implicit_cast(typename identity<Dst>::type t)
{ return t; }
The idea is to use a non-deduced context for the parameter t. That will avoid pitfalls like the following:
call_const_version(implicit_cast(this)); // oops, wrong!
What was desired is to write it out like this
call_const_version(implicit_cast<MyClass const*>(this)); // right!
The compiler can't deduce what type the template parameter Dst should name, because it first must know what identity<Dst> is, since it is part of the parameter used for deduction. But it in turn depends on the parameter Dst (identity could be explicitly specialized for some types). Now, we got a circular dependency, for which the Standard just says such a parameter is a non-deduced context, and an explicit template-argument must be provided.
Prefer implcit_cast if it is enough in your situation. implicit_cast is less powerful and safer than static_cast.
For example, downcasting from a base pointer to a derived pointer is possible with static_cast but not with implicit_cast. The other way around is possible with both casts. Then, when casting from a base to a derived class, use implicit_cast, because it keeps you safe if you confuse both classes.
Also keep in mind that implicit_cast is often not needed. Using no cast at all works most of the time when implicit_cast does, that's where 'implicit' comes from. implicit_cast is only needed in special circumstances in which the type of an expression must be exactly controlled, to avoid an overload, for example.
implicit_cast transforms one type to another, and can be extended by writing implicit cast functions, to cast from one type to another.
e.g.
int i = 100;
long l = i;
and
int i = 100;
long l = implicit_cast<long>(i);
are exactly the same code
however you can provide your own implicit casts for your own types, by overloading implicit_cast like the following
template <typename T>
inline T implicit_cast (typename mpl::identity<T>::type x)
{
return x;
}
See here boost/implicit_cast.hpp for more
Hope this helps
EDIT
This page also talks about implicit_cast New C++
Also, the primary function of static_cast is to perform an non changing or semantic transformation from one type to another. The type changes but the values remain identical e.g.
void *voidPtr = . . .
int* intPtr = static_cast<int*>(voidPtr);
I want to look at this void pointer, as if it was an int pointer, the pointer doesn't change, and under the covers voidPtr has exactly the same value as intPtr.
An implicit_cast, the type changes but the values after the transformation can be differnet too.
Implicit conversions, explicit conversions and static_cast are all different things. however, if you can convert implicitly, you can convert explicitly, and if you can convert explicitly, you can cast statically. The same in the other direction is not true, however. There is a perfectly reasonable relationship between implicit casts and
static casts. The former is a subset of the the latter.
See section 5.2.9.3 of the C++ Standard for details
Otherwise, an expression e can be
explicitly converted to a type T using
a static_cast of the form static_-
cast(e) if the declaration T t(e);
is well-formed, for some invented
temporary variable t (8.5).
C++ encourages use of static_casts because it makes the conversion 'visible' in the program. Usage of casts itself indicates some programmer enforced rule which is worth a look so better use static_cast.