Partial specialization of existing metafunction using mpl - c++

Maybe I'm not all there today, but I'm wondering how to get this to work.
I'd like to partially specialize range_mutable_iterator and range_const_iterator from the boost library but only for specific types that I'd rather avoid mentioning explicitly, instead only letting the partial specialization be chosen if the enable_if test criteria pass.
I'm currently using MSVC 2008 and am receiving the following error:
ArrayType: template parameter not used or deducible in partial specialization on type
range_mutable_iterator<
typename enable_if<
mpl::and_<
mpl::has_key<some_type_map, typename remove_const<T>::type>,
mpl::not_<is_const<T> >
>,
ArrayType
>::type
>
Using STLFilt, notice the odd reference to T instead of ArrayType, I'm guessing STLFilt is saying it can't figure out that T == ArrayType..? Here's what I have right now:
namespace boost {
template<class ArrayType>
struct range_mutable_iterator<
typename enable_if<
mpl::and_<
mpl::has_key<some_type_map, typename remove_const<ArrayType>::type>,
mpl::not_<is_const<ArrayType> >
>,/*and_*/
ArrayType
>::type/*enable_if*/
>
{
typedef MyArrayIterator<
typename mpl::at<some_other_type_map,
typename mpl::at<yet_another_type_map,ArrayType>::type
>::type/*at*/
>/*MyArrayIterator*/ type;
};
}
Getting range_begin/range_end working isn't currently an issue, the goal is to have a line work that looks like this:
ThirdPartyArrayClass blah;
MyArrayAdapter<ThirdPartyArrayClass>::iterator iter = boost::begin(blah);
Edit: After having tried a different approach which I've edited out of this answer, I came to accept that partial specialization in this case just isn't possible, so I've used a different approach which involves full specialization and heavy use of Boost.Preprocessor.

In order to partially specialize a template the compiler must match the actual template arguments to the existing specializations, with the aim to select the best match. This is just not possible if the template argument in the specialization is used just as the (template) parameter to a dependent type. So, the compiler complains, and rightly so. The only thing you can do is to somehow specialize on your concrete ThirdPartyArrayClass type.

Related

Something like std::integral_constant but with auto template argument in std C++20 library?

Starting from C++20 one can use auto template argument to implement integral constant:
Try it online!
template <auto Value>
struct integral_constant2
: std::integral_constant<decltype(Value), Value> {};
which can be used instead of more verbose variant std::integral_constant that has two template arguments.
Sure its easier to write f(std::integral_constant2<123>{}); instead of more verbose f(std::integral_constant<int, 123>{});. More than that you may not know type in advance if you have complex compile-time expression.
My question is whether there exists in C++20 std library anything like integral_constant2 mentioned above, not to reinvent wheel? Or at least some std constexpr function std::make_integral_constant(123) that deduces std::integral_constant's template params?
No, I am not aware of such replacement.
I believe it would be difficult to defend such proposal, given how easy it is to write your own. On the other hand the only reason might be that nobody proposed it yet.
Mainly as a curiosity, and expanding on a comment, you can take this one step further via:
#include <type_traits>
template <auto Value, template<typename A, A> typename C>
using automized = C< decltype(Value),Value>;
template <auto Value>
using integral_constant = automized<Value,std::integral_constant>;
int main() {
struct S {};
integral_constant<true> c0{};
integral_constant<10> c1{};
integral_constant<S{}> c2{};
}
automized would allow to forward an auto parameter to any template taking typename T, T value. However, it is rather limited because it only works for templates taking exactly those parameters, while getting the general case right is rather painful when type and non-type parameters can be mixed.
You can see all new feature in C++20 here : https://en.cppreference.com/w/cpp/20
and I don't see anything related to integral_constant (and I don't see anything in the type_traits page either)

How does enable_if help select specializations of a class template?

I have a basic grasp of SFINAE, and I think I understand many of the examples of how std::enable_if exploits it to select function template specializations, but I'm having a hard time wrapping my head around how it works for class templates.
The following example is from cppreference.com's explanation of std::enable_if:
template<class T, class Enable = void>
class A {}; // primary template
template<class T>
class A<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {
}; // specialization for floating point types
I'm having trouble understanding how using std::enable_if in this way helps select the specialization. (I don't doubt that it does.)
When the compiler sees a declaration like A<float> specialized;, it will see two possible template instantiations that fit:
The "primary template" A<T, Enable> where T is the type float and Enable is the type void (because of the default value).
The specialization A<T, void> where T is the type float and void is the result of the expression with the enable_if.
Aren't those ambiguous? Both effectively result in A<T, void>, so why is the specialization chosen?
In a different case, like A<int> primary;, the options to the compiler seem to be:
The primary, A<T, Enable>, where T is the type int and Enable is the type void.
The specialization, A<T, ?>, where T is the type int and the ? represents where I'm completely lost. In this case, the enable_if condition is false, so it doesn't define type, which leaves you with A<int, typename >. Isn't that a syntax error? Even in the face of SFINAE?
From the reference on partial specialization of class templates:
When a class or variable (since C++14) template is instantiated, and there are partial specializations available, the compiler has to decide if the primary template is going to be used or one of its partial specializations.
If only one specialization matches the template arguments, that specialization is used
In this case, if the 2nd argument of the specialization is well-formed, it is chosen, precisely because it is a specialization, and not the primary template.
In case the 2nd template argument is not well-formed, then SFINAE kicks in. In particular:
When substituting the explicitly specified or deduced type for the template parameter fails, the specialization is discarded from the overload set instead of causing a compile error.
and
The following type errors are SFINAE errors:
attempting to use a member of a type, where
the type does not contain the specified member
How this is done, i.e. how exactly the compiler discards the specialization, instead of giving an error, is not specified; the compiler is just required to do the right thing.
Aren't those ambiguous? Both effectively result in A<T, void>, so why is the specialization chosen?
No, the specialization is more specialized than the primary template, because it requires the second parameter to be void (assuming the enable_if condition is true), while the primary template doesn't restrict it.
The specialization, A<T, ?>, where T is the type int and the ? represents where I'm completely lost. In this case, the enable_if condition is false, so it doesn't define type, which leaves you with A<int, typename >. Isn't that a syntax error? Even in the face of SFINAE?
Exactly, the second argument in the specialization turns out invalid. But this is a "soft" error, which is detected by SFINAE and makes the compiler discard the specialization. (I don't think a compiler would textually replace enable_if_t<...>::type with an empty string and then analyze A<int, typename >; more likely it would discard the specialization as soon as it notices the lack of ::type in enable_if.)

meaning of c++ struct syntax with only typedef

When I examine some code snip from some libraries, I saw some code like this:
template<typename _Function, typename _ReturnType>
struct _TaskOfType_ContinuationTypeTraits
{
typedef task<typename _TaskTypeTraits<typename _FunctionTypeTraits<_Function, _ReturnType>::_FuncRetType>::_TaskRetType> _TaskOfType;
};
Can someone provide some explanation of the code? What is it trying to do and what is the advantage to use a struct with only typedef statement within the body?
In C++ parlance, the _TaskOfType_ContinuationTypeTraits is a metafunction. It does type computations at compile-time.
A metafunction is in a way similar to a run-time function. The key difference is that the input arguments to a metafunction are type(s), and returns are also type(s).
Eg. The following metafunction takes a type, and returns a pointer of the type you supply to it.
template <typename T>
struct add_pointer
{
typedef T* type;
}
Now, if you did add_pointer<int>::type, it returns int*. You see, you gave it a type (int in this case) and the compiler computed a new type (int* in this case) and gave it back to you when you invoked the ::type of the metafunction. When you do ::type on a metafunction, that is when the template is instantiated. This is the run-time equivalent of calling a function. Also note that all of this happened at compile-time!
Now, going back to your _TaskOfType_ContinuationTypeTraits. This is just like my add_pointer. In add_pointer, I just had one template argument, you have two. I just added a pointer to the type that was supplied, you have something much more complicated. But, at it's essence, it is only a type computation. My add_pointer returns when I call ::type on it, yours does when you call ::_TaskOfType.
This kind of syntax is used to create "templated typedefs". In C++11 and above, type aliases / alias templates should be used instead.
The purpose of the code snippet you posted is to create a type alias that depends on _Function and _ReturnType.
It can be accessed like this:
typename _TaskOfType_ContinuationTypeTraits<F, R>::_TaskOfType
If you have access to C++11, this is a cleaner, better and more straightforward solution:
template<typename _Function, typename _ReturnType>
using _TaskOfType =
task<typename _TaskTypeTraits<
typename _FunctionTypeTraits<_Function, _ReturnType>::_FuncRetType>::_TaskRetType>
Which can be used like this:
_TaskOfType<F, R>
More info: "Difference between typedef and C++11 type alias"

Clang fails to compile parameter pack expansion using template metaprogramming

I have a boost::variant of several ranges. In this context, a range is just a std::pair<It, It>, where It is an iterator. I use this to store ranges of iterators satisfying certain properties.
Since I don't know the iterator types, I use a little template meta-programming to obtain the first_type of the std::pair, since I need a second boost::variant containing a single iterator (corresponding to some active element of that type).
The following code is simplified to help with the question, but consider that I have an unknown number of ranges in my RangeVariant (which means I can't create it manually, as I can do for this particular case).
#include <utility>
#include <vector>
#include <boost/variant.hpp>
template <class A, template <typename...> class B>
struct FirstTypeVariantImpl;
template <template <typename...> class A, typename... Pair, template <typename...> class B>
struct FirstTypeVariantImpl<A<Pair...>, B> /*! specialization */
{
using type = B<typename Pair::first_type...>;
};
template <class A, template <typename...> class B>
using FirstTypeVariant = typename FirstTypeVariantImpl<A, B>::type;
int main()
{
using Container = std::vector<int>;
using Range = std::pair<Container::iterator, Container::iterator>;
using RangeVariant = boost::variant<Range>;
using IteratorVariant = FirstTypeVariant<RangeVariant, boost::variant>;
};
The above program compiles correctly with gcc, but fails with clang. The error I get is the following:
program.cpp:12:29: error: incomplete type 'boost::detail::variant::void_' named in nested name specifier
using type = B<typename Pair::first_type...>;
^~~~~~
program.cpp:16:1: note: in instantiation of template class 'FirstTypeVariantImpl<boost::variant<std::pair<__gnu_cxx::__normal_iterator<int *, std::vector<int, std::allocator<int> > >, __gnu_cxx::__normal_iterator<int *, std::vector<int, std::allocator<int> > > >, boost::detail::variant::void_, ..., boost::detail::variant::void_>, variant>' requested here
using FirstTypeVariant = typename FirstTypeVariantImpl<A, B>::type;
^
program.cpp:23:29: note: in instantiation of template type alias 'FirstTypeVariant' requested here
using IteratorVariant = FirstTypeVariant<RangeVariant, boost::variant>;
^
../../../include/boost/variant/variant_fwd.hpp:193:8: note: forward declaration of 'boost::detail::variant::void_'
struct void_;
^
So, it seems clang is attempting to obtain the first_type of boost::detail::variant::void_, but somehow gcc recognizes it and ignores it. Something similar happens if I obtain the type for the first element using the <tuple> header:
using type = B<typename std::tuple_element<0, Pair>::type...>;
The error after this change is different, but again related to clang trying to apply the operation to boost::detail::variant::void_:
program.cpp:13:34: error: implicit instantiation of undefined template 'std::tuple_element<0, boost::detail::variant::void_>'
using type = B<typename std::tuple_element<0, Pair>::type...>;
I'm using boost 1.57.0, gcc 4.8.3 and clang 3.6.0, always using -std=c++11 with -Wall -Werror -Wextra flags. Using other versions of either of these is not an option :-(
Any help would be appreciated. I don't even know whether this is a bug in clang or boost, or even in gcc, if my usage is incorrect. Thanks in advance for your help.
We agree on that void_ is part of boost::variant's pre-variadic template workaround (every instantiation is boost::variant<MandatoryType, ⟪boost::detail::variant::void_ ⨉ 𝖫𝖨𝖲𝖳_𝖲𝖨𝖹𝖤 ⟫>).
Now, the thing is that using metashell I found out there exists at least one version of boost::variant that does not use this workaround.
Looking around, I found that there was a bug recently fixed about how boost libs do not recognize clang's variadic template capability correctly.
To answer your question: gcc compiles because boost libs recognize variadic template availability, while missing clang's. This results in void_ failing to be instantiate in your meta-programming tangle as this struct has been declared, but not defined.
The reason this doesn't work is that boost::variant isn't implemented the way you think it is.
boost::variant like all of boost is compatible with C++03, before the time when there were variadic templates.
As a result, boost::variant has to work around the lack of that language feature, by imposing a maximum number of variants and using only C++03 template features.
The way they do this is, the template has 20 template arguments, and they all have a default value of boost::variant::detail::void_.
Your variadic capture is catching those extra parameters, just the same way that if you tried to capture all the parameters to std::vector you would get your type, also an allocator, etc., even if you didn't explicitly specify an allocator.
The work arounds I can think of off-hand are,
1) Don't use boost::variant, use a C++11 variant based on variadic templates. There are many implementations floating around.
2) Use boost variant, but also create a type-trait that permits you recover the original parameter pack from a typelist. You would have to make sure that every time you instantiate it, you also create an entry in the type trait, but you can use a macro to make sure that happens.
3) There may be a way to make boost::variant use an implementation based on variadic templates? But I'm not sure of this, I would have to review the docs. If there is then it means there is some preprocessor define you can use to force this.
Edit: The macro is this actually:
http://www.boost.org/doc/libs/1_60_0/doc/html/BOOST_VARIANT_DO_NOT_USE_VARIADIC_TEMPLATES.html
So in recent versions of boost, you have to request explicitly not to have the variadic implementation, unless you are on C++03 presumably?
You might want to explicitly check if something in one of your headers is defining this for some reason.
Although both Chris' and Felipe's contributions answer my question partially (thanks guys!), here is an update that actually compiles with the Boost and clang versions I mentioned.
First, update the specialization of FirstTypeVariant so that it obtains the type from another structure instead of directly obtaining T::first_type:
template <template <typename...> class A, typename... Pair, template <typename...> class B>
struct FirstTypeVariantImpl<A<Pair...>, B> /*! specialization */
{
using type = B<typename ObtainFirstType<Pair>::type...>;
};
Then, specialize the ObtainFirstType struct so that it returns the iterator type for std::pair<T, T> (remember that in my use case, T is an iterator).
template <typename T>
struct ObtainFirstType
{
using type = T;
};
template <typename T>
struct ObtainFirstType<std::pair<T, T>>
{
using type = T;
};
Now, this will compile and work, but there's a caveat. The number of elements of the variant with clang will always be 20, so any algorithm depending on that might change its behavior. We can count them like this:
template <typename... Ts>
struct VariantSize
{
static constexpr std::size_t size = 0;
};
template <typename... Ts>
struct VariantSize<boost::variant<Ts...>>
{
static constexpr std::size_t size = sizeof...(Ts);
};
In my example, I created a variant with 3 elements, which I then counted:
int main()
{
using ContainerA = std::vector<int>;
using ContainerB = std::vector<double>;
using ContainerC = std::vector<bool>;
using RangeA = std::pair<ContainerA::iterator, ContainerA::iterator>;
using RangeB = std::pair<ContainerB::iterator, ContainerB::iterator>;
using RangeC = std::pair<ContainerC::iterator, ContainerC::iterator>;
using RangeVariant = boost::variant<RangeA, RangeB, RangeC>;
using IteratorVariant = FirstTypeVariant<RangeVariant, boost::variant>;
std::cout << "RangeVariant size : " << std::to_string(VariantSize<RangeVariant>::size) << std::endl;
std::cout << "IteratorVariant size : " << std::to_string(VariantSize<IteratorVariant>::size) << std::endl;
};
The output with GCC is
RangeVariant size : 3
IteratorVariant size : 3
while the output with CLANG is the following:
RangeVariant size : 20
IteratorVariant size : 20

Why doesn't this simple enable_if work?

I'm having a problem with an enable_if construct which I've managed to reduce to a very simple piece of failing code:
template <typename Enable, typename...Args>
struct Get;
template <typename FirstArg, typename... OtherArgs>
struct Get<typename std::enable_if<true>::type, FirstArg, OtherArgs...>
{
using type = FirstArg;
};
(Live example)
The above code is a gutted version of some code that did something useful, but for the sake of this question I'm more interested in why this doesn't work, not whether or not it's ideal or does anything useful. The meta function Get should take the first type passed to it and, based on some condition (in this case always true as I didn't construct a case for when it is not) return it.
I don't want the first argument to resolve to the enable_if condition, that should be completely abstracted.
When I try to run this (see live example) it produces
error: ‘type’ in ‘struct Get’ does not name a type using T =
typename Get::type
Why?
The specialization you wrote only kicks in if someone passed void as the first argument to Get.
In your example code, you passed int. So the specialization does not apply.
The names of the types in the template definition and specialization have no connection, just their position in the Get<blah, blah, blah>. And it is pattern matching off the primary definition.
int main() {
using T = Get<void, int>::type;
}