Better pack expansion syntax in variadic template? - c++

It might be not very efficient to talk about the language syntax that has already been set in stone.
I would, however, like to see why the C++11 variadic template's argument expansion couldn't be more explicit to read when it comes to some nested usage.
For example, I think C++ developers could've generally written code more clearly as follows.
(If that's allowed)
template<typename ...> struct Tuple
{
};
template<typename T1, typename T2> struct Pair
{
};
template<class ...Args1> struct zip
{
template<class ...Args2> struct with
{
typedef Tuple<Pair<Args1, Args2>...> type;
// More code but but maybe better readability?
// because Args1, and Args2 are the ones that get
// actually expanded, not Pair<T1, T2>.
// typedef Tuple<Pair<Args1..., Args2...>> type; // ## Invalid! ##
};
};
typedef zip<short, int>::with<unsigned short, unsigned>::type T1;
By doing so we don't really have to get confused which template types are exactly being expanded or should be the ones that will have to be expanded when making multiple-nested variadic template, because then it becomes more important for your code to look clearer than being shortened.
Plus I'd love to see the code in the same manner where I call a function as follows:
Foo(args...); // Okay.
We don't do like this:
Foo(args)...; // Error.
EDIT
I believe this one should've been at least allowed to work nicely.
Tuple<Pair<Args1..., Args2...>> // Invalid code.
But it does not.
Why not?
Is there any potential risk to convince the committee enough to prevent it from compiling?
Thank you so much in advance.

In every one of your cases, your "preferred" notation does a different expansion than the one you want. They aren't invalid.
If Args0 = { a,b,c } and Args1 = { int,double,char } then:
std::tuple< foo< Args0..., Args1... > >
is
std::tuple< foo< a,b,c,int,double,char > >
while
std::tuple< foo< Args0, Args1>... >
is
std::tuple< foo< a, int >, foo< b, double >, foo< c, char > >
The thing is, these are both useful operations.
The key is ... operates on potentially-expanded sub-"expressions", not on packs directly.
Unless your proposed notation can generate both of these results, it is a weaker notation.
The same thing holds true with Foo(Args)... and Foo(Args...). They aren't alternate ways to say something -- they are ways of saying two different things in current C++. Your plan results in in the 2nd meaning the 1st, which means there is no longer a way to express what the 2nd means in your version of C++.
There are two parts to each expansion. The first is what packs are being expanded, the second is what "expression" is being expanded. The packs being expanded, in current C++, are all unexpanded packs within the expression: they are not otherwise marked.
In your version, you mark the packs to expand, and never mark the expression to be expanded. If the language read your mind, it could figure out where that expression was, but programming languages that read your mind are usually a bad idea.
A side effect of "mark the expression, not the pack" is that you can do some seriously powerful stuff, like have entire lambdas that contain unexpanded packs inside expressions inside the body, and then generate a pack of lambdas. Expressing that in your system would be challenging.

Related

C++ Variadic Templates for a General-Purpose and Fast Data Storage Container Builder

template< typename ... Args>
auto build_array(Args&&... args) -> std::array<typename std::common_
type<Args...>::type, sizeof...(args)>
{
using commonType = typename std::common_type<Args...>::type;
return {std::forward<commonType>(args)...};
}
int main()
{
auto data = build_array(1, 0u, 'a', 3.2f, false);
for(auto i: data)
std::cout << i << " ";
std::cout << std::endl;
}
Hey guys, I cannot understand the above code. So basically, the code is to write a function that takes any number of elements of any type, which can, in turn, be converted into a common type. The function should also return a container having all the elements converted into that common type, and it should also be fast to traverse. This is a books solution.
From what I understand <typename... Args> is to allow a variation of parameters. Then, (Args&&...args) also allows for a variety of parameters, but only rvalues? I do not understand the arrow notation and the rest of the function declaration. Like what is the difference between each of them. Additionally, the book also passes in ? for the templates such as, std::array<?,?>?
Finally, what does the return statement even mean (ending with an ellipsis?) ? and forward?
Sorry, I am rambling on, but I just cannot make sense and obtain a detailed overview of what is going on.
It would be really kind of you if you can elaborate on this?
but only rvalues?
When you see T&&,
if T is not a template parameter, then T&& means an rvalue reference to T, which can bind to rvalules only;
if T is a template parameter, then T&& means a forwarding/universal reference to T, which can bind to both rvalue and lvalues.
Therefore, in your case, since Args is a template parameter (precisely a type template parameter pack, number (2) here), Args&&... args expands to a comma separated sequence of function parameter declarations each of which has type a forwarding reference. For instance, if you pass 3 arguments to build_array, the deduction takes place as if you had a declaration like this:
template<typename Arg1, typname Arg2, typname Arg3>
auto build_array(Arg1&& arg1, Arg2&& arg2, Arg3&& arg3)
-> std::array<typename std::common_type<Arg1, Arg2, Arg3>::type, 3>
what does the return statement even mean (ending with an ellipsis?) ?
Again, ... is to expand some variadic thing in a comma separated sequence of things. So if args in
return {std::forward<commonType>(args)...};
is actually 3 things, then that statement is expanded to
return {std::forward<commonType>(arg1), std::forward<commonType>(arg2), std::forward<commonType>(arg3)};
Notice the position of the ellipsis. f(args)... is expanded to f(arg1), f(arg2), f(arg3), …, whereas f(args...) would be expanded to f(arg1, arg2, arg3, …).
and forward?
That's probably the less easy to understand bit, and would require a dedicated question. However many questions on that topic exist already, so you just have to search for them, rather than asking a new one. Here an answer of mine where I've most clearly explained the difference between std::move and std::forward. If you set understanding that answer of mine as your target, you'll understand std::forward (and std::move) and everything will be clearer.
I do not understand the arrow notation and the rest of the function declaration.
Essentially,
auto f(/* parameters */) -> SomeType
is equivalent to
SomeType f(/* parameters */)
with the advantage that in the former SomeType can refer to types that are in /* parameters */, if needed. In your case the return type makes use of Args.
the book also passes in ? for the templates such as, std::array<?,?>?
Probably the book is just trying to guide you through argument deduction, and it's using ? to mean "we don't know yet what it is; keep reading".

How do I correctly use enable_if in this specific function declaration?

I have a class defined as:
template <typename V, typename E>
class AdjacencyList;
Where V and E are the types of the vertex and edge values respectively.
I am currently attempting to define the following member function inside AdjacencyList:
std::map< std::shared_ptr< Vertex<V, E> >, E > dijkstra(
const std::shared_ptr< Vertex<V, E> > &start_vertex) const;
For those familiar with Dijkstra's algorithm, it is only possible to implement correctly if E is an addable and non-negative type. Therefore, how do I correctly use the enable_if construct to only enable this function if E is an unsigned integral type?
I am currently seeing two complications here that I am uncomfortable with approaching:
Both the return type and the parameter concern E.
E itself is not used as a type, but is used in other type templates.
Because I am relatively new to the enable_if construct, I would feel more comfortable with some guidance on the issue since this is a relatively non-trivial case.
This is not actually what you want to do.
The point of std::enable_if is to cause substitution failure for a template. The reason why you want substitution failure is because it's not a failure, and you can select a different overload instead. However, there's no point here because you're not trying to choose a different overload, you're just trying to make it a failure.
So, you'd instead do something like this:
std::map< std::shared_ptr< Vertex<V, E> >, E > dijkstra(
const std::shared_ptr< Vertex<V, E> > &start_vertex) const {
static_assert(
std::is_unsigned<E>::value,
"E must be unsigned.");
}
If you try to call this function with the wrong type of arguments, you get a nice compile-time error telling you that E must be addable and non-negative. If you used enable_if instead, you'd get an error that none of the overloads were valid, which is a less informative error.
This is probably a bad design, though. Ordinarily you would just throw an exception if you encountered a negative value. Choosing to enforce that the inputs are positive is also incomplete, since the algorithm will also fail if you encounter overflow.
(If you really want to do enable_if even though it's a bad idea, you can..)
std::enable_if<std::is_unsigned<E>,
std::map<std::shared_ptr<Vertex<V, E>>, E>::type dijkstra...
If you actually want to program this way
C++ is the wrong language for this type of programming, and hammering C++ until it works this way will result in some very bizzare C++ code. It sounds like you actually want to write code in Agda.
enable_if is helpful if you want to write several overloads with adjusted type requirements. In your case you don't have any overloads and only want to enforce some type restrictions. So you can just use static_assert to check them and give user a meaningful diagnostic message instead of usual template instantiation failure mess. Something like this:
<type_traits>
<utility>
...
static_assert(::std::is_unsigned< E >::value, "edge value type must be unsigned integral type");
static_assert(::std::is_same< E, decltype(::std::declval< E >() + ::std::declval< E >()) >, "edge value type must be addable");
only enable this function if E is an unsigned integral type
I get your comment/request as it is and use directly the tools provided by <type_traits>.
If you want to use std::enable_if, probably the following works almost fine for you:
std::enable_if_t<std::is_integral<E>::value and std::is_unsigned<E>::value, std::map< std::shared_ptr< Vertex<V, E> >, E >> dijkstra( const std::shared_ptr< Vertex<V, E> > &start_vertex) const;
Anyway, SFINAE (Substitution Failure Is Not An Error) makes sense when you have more than one option, otherwise you are doing something we can call Substitution Failure Is Always An Error. In these cases, a static_assert is more appropriate:
static_assert(std::is_integral<E>::value and std::is_unsigned<E>::value, "!");
Put it as the very first line of your function. Error messages with static asserts are also usually more user friendly.

Is it possible to use binary logic in templates in short way?

Say I want do something for integral types but not chars and I have
is_integral<T>::type and is_char<T>::type
Is it possible to write this:
integral_constant<bool,is::integral<T>::value && !is_char<T>::value>
more readable
Those kinds of metacomputations were done even before C++11 and Boost.MPL is the heavy artillery of TMP in C++03. With it, your requirements can be expressed like so:
// in C++03:
// typedef /* compute */ result;
using result = and_<is_integral<T>, not_<is_char<T>>>;
(Where and_ and not_ are from the boost::mpl namespaces.)
Notice the limited verbosity because you don't have to put with the ::value boilerplate. Similarly, result is lazy, where you can force computing the result with either result::value (which would be true or false) or result::type (which would be, well, a type -- the documentation has all the details). Boost.MPL makes it easy not to do that though, so for instance not_<result> is enough to invert the logic, even though not_<result::type> would also work. A good thing considering that an additoinal typename is needed if result is dependent.
I do consider Boost.MPL of enormous help in C++03 partly because it emulates variadic templates. For instance, and_ is not restricted to two arguments. I've shied away from it in C++11 however because in most situations where I used it I now use pack expansion. Things like and_ are still useful though because it is not possible to expand a pack in arbitrary expressions, e.g. in one that involves the && logical operator. This can only be done with a logical metafunction:
// Enforce precondition: every type T must be integral
static_assert( and_<std::is_integral<T>...>::value, "Violation" );
I consider this article a good read that gives good hints to reduce the verbosity of metacomputations. It focuses on SFINAE as used for generic programming but it is applicable to TMP as well (and SFINAE can be used in TMP as well). The final example is
template <typename T,
EnableIf<is_scalable<T>, is_something_else<T>>...>
T twice(T t) { return 2*t; }
which could look like this in C++03, using facilities similar to those provided in Boost:
template<typename T>
typename enable_if<
and_<is_scalable<T>, is_something_else<T>>
, T
>::type twice(T t) { return 2*t; }
A naive C++11 version arguably looks the worst:
template<typename T>
typename std::enable_if<
is_scalable<T>::value && is_something_else<T>::value
, T
>::type twice(T t) { return 2*t; }
I've seen some that advocate moving away from the style of Boost.MPL, which favours types and type-'returning' metafunctions towards using values and constexpr functions. This could look like:
// EnableIf alias now accepts non-type template parameters
template<typename T
, EnableIf<is_scalable<T>() && is_something_else<T>()>...>
T twice(T t) { return 2*t; }
You'd still need a constexpr function to compute e.g. the logical disjunction if T... is a pack though: EnableIf<any(is_foo<T>()...)>.
Why do you need an integral constant? If you just need a compile-time constant bool then you don't have to wrap the expression.
is_integral<T>::value && !is_char<T>::value
If you need this expression in several places you can just write you own special type trait.
template<typename T>
struct is_integral_and_not_char {
static const bool value = is_integral<T>::value && !is_char<T>::value;
};
is_integral_and_not_char<T>::value
Or if you do want to conform to the UnaryTypeTrait concept then
template<typename T>
struct is_integral_and_not_char
: std::integral_constant<bool, std::is_integral<T>::value && !std::is_char<T>::value>
{}

Why are C++11 type traits not alias templates?

Similar question: Why are type_traits implemented with specialized template structs instead of constexpr? – but with a different answer.
I realise that alias templates cannot be specialised and hence can’t currently be used to implement type traits directly1. However, this is a conscious decision of the committee, and as far as I see there is no technical reason to forbid this.
So wouldn’t it have made more sense to implement type traits as alias templates, streamlining their syntax?
Consider
typename enable_if<is_pointer<T>::value, size_t>::type
address(T p);
versus
enable_if<is_pointer<T>, size_t> address(T p);
Of course, this introduces a breaking interface change when moving from Boost.TypeTraits – but is this really such a big problem?
After all, the code will need to be modified anyway since the types reside in different namespace and, as many modern C++ programmers are reluctant to open namespaces, will be qualified explicitly (if it would be changed at all).
On the other hand, it vastly simplifies the code. And given that template metaprogramming often gets deeply nested, convoluted and complex, it seems obvious that a clearer interface is beneficial.
Am I missing something? If not, I’d appreciate an answer that is not mere guesswork but relies on (and can cite) knowledge of the committee’s decision rationale.
1 But very well indirectly! Consider:
template <typename T> using is_pointer = typename meta::is_pointer<T>::type;
Where meta::is_pointer<T> corresponds to the current std::is_pointer<T> type.
The most concrete answer to your question is: No one ever proposed doing it that way.
The C++ standards committee is a multi-national, multi-corporation collection of volunteers. You're thinking of it as a design committee within a single organization. The C++ standards committee literally can not do anything without a proposal to put words into the draft standard.
I imagine that the reason there was no proposal is that type traits was an early proposal, with the boost implementation dating back to around 2000. And template aliases were late in getting implemented. Many of the committee members are reluctant to propose something that they have not implemented. And there was simply little opportunity to implement your proposal.
There was a lot of pressure to ship C++11. It really was intended to ship in 2009 and when that ship date slipped, it was very tough to do anything to the working paper besides fix the features already under consideration. At some point you've got to put great new ideas on the back burner, lest you will never ship.
Update
As of C++14, the TransformationTraits (those which result in a type) now have template alias spellings, for example:
template <bool b, class T = void>
using enable_if_t = typename enable_if<b,T>::type;
And the C++1z working draft now has template variable spellings for the traits resulting in values:
template <class T>
constexpr bool is_pointer_v = is_pointer<T>::value;
Also, even in C++11 one could do:
typename enable_if<is_pointer<T>{}, size_t>::type
address(T p);
I.e. you can use {} in place of ::value (assuming your compiler has constexpr support). In C++14 that becomes:
enable_if_t<is_pointer<T>{}, size_t>
address(T p);
And in C++1z:
enable_if_t<is_pointer_v<T>, size_t>
address(T p);
Note that the difference between C++1z and C++14 is so minimal that it doesn't even save characters, just changes {} to _v and changes where you put these two characters.
Type traits, like several other libraries including <memory> and <functional>, were inherited from C++ TR1. Although that was a less formal document, it was more formal than Boost, and compatibility is worthwhile.
Also, note that type traits are all derived from std::integral_constant<bool>, which does implement a constexpr conversion function to bool. So that at least saves the ::value parts, if you so choose.
As a complete side-note since there seems to be confusion on how aliases may or may not help a trait like std::is_pointer:
You can go the Boost.MPL route and decide that you'll use Boost.MPL-style integral constants, which means types
template<typename Cond, typename Then = void>
using enable_if = typename std::enable_if<Cond::value, Then>::type;
// usage:
template<
typename T
, typename = enable_if<std::is_pointer<T>>
>
size_t address(T p);
or you can decide to use values instead
template<bool Cond, typename Then>
using enable_if = typename std::enable_if<Cond, Then>::type;
// can use ::value
template<
typename T
, typename = enable_if<std::is_pointer<T>::value>>
>
size_t address(T p);
// or constexpr conversion operator
template<
typename T
, typename = enable_if<std::is_pointer<T> {}>
>
size_t address(T p);
Note that in the latter case it's not possible to use enable_if<std::is_pointer<T>()>: std::is_pointer<T>() is a function type (taking void and returning std::is_pointer<T>) and is invalid since our alias takes a value and not a type in this case. The braces ensure that it is a constant expression instead.
As you may have noticed, std::is_pointer doesn't benefit from template aliases at all. This isn't surprising at it's a trait where the interesting part is accessing ::value, not ::type: template aliases can only help with member types. The type member of std::is_pointer isn't interesting since it's an Boost.MPL-style integral constant (in this case either std::true_type or std::false_type), so this doesn't help us. Sorry!

Use static_assert to check types passed to macro

I unfortunately have several macros left over from the original version of my library that employed some pretty crazy C. In particular, I have a series of macros that expect certain types to be passed to them. Is it possible to do something along the lines of:
static_assert(decltype(retval) == bool);
And how? Are there any clever alternatives?
Yes I'm aware macros are bad. I'm aware C++ is not C, etc.
Update0
Here is some related code, and the source file. Suggestions are welcome. The original question remains the same.
I found this to be the cleanest, using #UncleBens suggestion:
#include <type_traits>
static_assert(std::is_same<decltype(retval), bool>::value, "retval must be bool");
Disclaimer: This is a bad answer, there are definitely far better solutions. Just an example :)
It is bound to be already implemented, but it's trivial to implement yourself;
template <class T1, class T2> struct CheckSameType; //no definition
template <class T> struct CheckSameType<T,T>{}; //
template <class T1, class T2>
AssertHasType(T2)
{
CheckSameType<T1, T2> tmp; //will result in error if T1 is not T2
}
To be used like this:
AssertHasType<bool>(retval);
Alternative (suggested by GMan):
template <class T1, class T2> struct SameType
{
enum{value = false};
}
template <class T> struct SameType<T,T>
{
enum{value = true};
};
To be used like
static_assert(SameType<decltype(retval), bool>::value);
It appears you need decltype because you've got an expression, but want to verify a type. There are already enough ways to do that now (C++03). For instance, to check a bool
inline void mustBeBool(bool) { }
template<typename T> inline void mustBeBool(T t) { & (&t); } // Takes address of rvalue (&t)
// Use:
#define DifficultMacro(B) do { mustBeBool(B); foo(B); } while (false)
If you DO care about the const and volatile qualifiers, and want to ensure the const and volatile parts of the types also exactly match the type you are comparing against, do like #Matt Joiner says:
#include <type_traits>
static_assert(std::is_same<decltype(my_variable), uint64_t>::value,
"type must be `uint64_t`");
I you do NOT care about const, however, and want to simply ensure the type is a certain type without regard for const, do the following instead. Note that std::remove_const<>::type is required here:
static_assert(std::is_same<std::remove_const<decltype(my_variable)>::type, uint64_t>::value,
"type must be `uint64_t` OR `const uint64_t`");
The same goes for volatile. In case you don't care about the volatile part of the type either, you can ignore it with std::remove_volatile<>::type:
static_assert(std::is_same<std::remove_volatile<decltype(my_variable)>::type, uint64_t>::value,
"type must be `uint64_t` OR `volatile uint64_t`");
If you don't care about const OR volatile, you can remove them both with std::remove_cv<>::type:
static_assert(std::is_same<std::remove_cv<decltype(my_variable)>::type, uint64_t>::value,
"type must be `uint64_t` OR `const uint64_t` OR `volatile uint64_t` OR `volatile const uint64_t`");
Note also that as of C++17 you can do:
std::remove_cv_t<decltype(my_variable)> in place of std::remove_cv<decltype(my_variable)>::type, and:
std::is_same_v<some_type, another_type> in place of std::is_same<some_type, another_type>::value.
References:
Use static_assert to check types passed to macro
https://en.cppreference.com/w/cpp/types/is_same
https://en.cppreference.com/w/cpp/language/decltype
https://en.cppreference.com/w/cpp/header/type_traits
https://en.cppreference.com/w/cpp/types/remove_cv - std::remove_cv<>, std::remove_const<>, std::remove_volatile<>
Related:
[another answer of mine where I use the above static_assert tricks] How to make span of spans
Static assert in C [my own answer]
How to use static assert in C to check the types of parameters passed to a macro [my own question]
Typechecking macro arguments in C
*****C++ Limit template type to numbers
Most macros can be replaced with inline functions and/or templates. As a case in point, the overly clever argument-size-checking Posix isnan macro is a template in C++0x. Oh,bad example, but you get the idea.
The main exceptions to that rule are macros that essentially implement higher level language features. For example, smarter exception handling, or covariance, or a parameterized set of declarations.
In some cases the macros that can't be reasonable expressed as inline functions or templates, can be replaced with a smarter kind of preprocessing, namely code generation. Then you have a script somewhere that generates the necessary code. For example, it's possible to do options classes in pure C++ with macros and templates, but it's hairy, and as an easier-to-grok and perhaps more maintainable alternative one might use a script that generates the requisite classes, at the cost of extra build steps and dealing with multiple languages.
Cheers & hth.,