I recently migrated a C++ code from VS2012 to VS2013. The code was compiling under VS2012 but VS2013 throws a C1001 Internal compiler error.
Specifically, the error points to the tuple.h file from the std library:
template<class... _Types1,
class _Kx_arg,
size_t... _Ix,
size_t _Ix_next,
class... _Types2,
class... _Rest>
struct _Tuple_cat2<tuple<_Types1...>, _Kx_arg, _Arg_idx<_Ix...>, _Ix_next,
tuple<_Types2...>, _Rest...>
: _Tuple_cat2<
tuple<_Types1..., _Types2...>,
typename _Cat_arg_idx<_Kx_arg,
typename _Make_arg_idx<_Types2...>::type>::type,
_Arg_idx<_Ix..., _Repeat_for<_Ix_next, _Types2>::value...>,
_Ix_next + 1,
_Rest...>
{ // determine tuple_cat's return type and _Kx/_Ix indices
};
My code calls the std::tuple_cat method in order to retrieve the type of concatenated tuples (note the partial specializations with the void type):
template <typename TupleA, typename TupleB>
struct tuple_concatenator//yields the type of two concatenated tuples.
{
typedef decltype(std::tuple_cat(std::declval<TupleA>(),
std::declval<TupleB>())) type;
};
template <typename TupleA>
struct tuple_concatenator<TupleA, void>//yields the type of TupleA.
{
typedef TupleA type;
};
template <typename TupleB>
struct tuple_concatenator<void, TupleB>//yields the type of TupleB.
{
typedef TupleB type;
};
template <>
struct tuple_concatenator<void, void>
{
typedef void type;
};
How would you configure VS2013 or rewrite the aforementioned code to avoid the C1001 error?
Thank you in advance for your help.
An ICE is always a compiler bug. File a bug with Microsoft (also, try and see if you can reproduce it on http://webcompiler.cloudapp.net/, which runs VS2015 RC).
std::tuple_cat is tricky to implement because it needs to be able to efficiently concatenate an arbitrary number of tuples - both the type and the value. But if you just need to concatenate two std::tuple<...> types, you don't need the complicated tuple_cat machinery; it's straightforward:
template <typename TupleA, typename TupleB>
struct tuple_concatenator;
template <class... Ts, class... Us>
struct tuple_concatenator<std::tuple<Ts...>, std::tuple<Us...>>
{
typedef std::tuple<Ts..., Us...> type;
};
This should work just fine with your existing partial and full specializations for void.
Related
Here is some code that compiles in GCC (on godbolt at least - can't test locally), for handling a compile-time dependency system - the conversion operator here is to make it easier to take an entity that specifies what it can read/write and reduce it down implicitly to a more restricted form when passing into functions (please pretend that the operator below does some static_assert kind of enforcement).
template<typename... Args>
struct WriteList{};
template<typename... Args>
struct ReadList{};
template<typename Reads = ReadList<>, typename Writes = WriteList<>>
class TypedEntity;
template <typename... ReadTypes, template <typename...> typename Reads, typename... WriteTypes, template <typename...> typename Writes>
class TypedEntity<Reads<ReadTypes...>, Writes<WriteTypes...>>
{
public:
template <typename... OtherReadTypes, typename... OtherWriteTypes>
operator TypedEntity<ReadList<OtherReadTypes...>, WriteList<OtherWriteTypes...>>()
{
return {};
}
};
struct ComponentA{};
struct ComponentB{};
struct ComponentC{};
void TestFunc1(TypedEntity<ReadList<ComponentA, ComponentB>, WriteList<ComponentC>> entity)
{
}
void TestFunc2(TypedEntity<ReadList<ComponentA>, WriteList<>> entity)
{
}
void TestFunc3(TypedEntity<ReadList<ComponentA>, WriteList<ComponentC>> entity)
{
}
int main()
{
TypedEntity<ReadList<ComponentA, ComponentB>, WriteList<ComponentB>> entity;
TestFunc1(entity);
TestFunc2(entity);
TestFunc3(entity);
return 0;
}
But under MSVC (latest, i.e.g 19.28, as well as some other 19.x versions I've sampled (19.14, 19.24, etc)):
error C3547: template parameter 'OtherWriteTypes' cannot be used because it follows a template parameter pack and cannot be deduced from the function parameters of 'TypedEntity<Reads<ReadTypes...>,Writes<WriteTypes...>>::operator TypedEntity<ReadList<OtherReadTypes...>,WriteList<OtherWriteTypes...>>'
Is this valid C++ and MSVC is wrong?
Is there a workaround for this issue in MSVC?
Appreciate it.
The error message hints at MSVC's mistake (emphasis mine):
error C3547: template parameter 'OtherWriteTypes' cannot be used because it follows a template parameter pack and cannot be deduced from the function parameters of 'TypedEntity<Reads<ReadTypes...>,Writes<WriteTypes...>>::operator TypedEntity<ReadList<OtherReadTypes...>,WriteList<OtherWriteTypes...>>'
It's true it can't be deduced from the function parameters - but for a conversion function, deduction happens from the return type, not the empty parameter list.
So a workaround is to simplify the template signature of the conversion function. Presuming the implementation needs to know the actual ReadTypes... and WriteTypes..., the operator() definition can just call a private ordinary member function which can deduce them from parameters.
template<typename T>
struct is_WriteList_s : public std::false_type {};
template<typename... Args>
struct is_WriteList_s<WriteList<Args...>> : public std::true_type {};
template<typename T>
concept is_WriteList = is_WriteList_s<T>::value;
template<typename T>
struct is_ReadList_s : public std::false_type {};
template<typename... Args>
struct is_ReadList_s<ReadList<Args...>> : public std::true_type {};
template<typename T>
concept is_ReadList = is_ReadList_s<T>::value;
template <typename... ReadTypes, template <typename...> typename Reads,
typename... WriteTypes, template <typename...> typename Writes>
class TypedEntity<Reads<ReadTypes...>, Writes<WriteTypes...>>
{
private:
template <typename... OtherReadTypes, typename... OtherWriteTypes>
TypedEntity<ReadList<OtherReadTypes...>, WriteList<OtherWriteTypes...>>
convert_impl(std::type_identity<TypedEntity<
ReadList<OtherReadTypes...>, WriteList<OtherWriteTypes...>>>) const;
public:
template <is_ReadList OtherReadList, is_WriteList OtherWriteList>
operator TypedEntity<OtherReadList, OtherWriteList>() const
{
return convert_impl(
std::type_identity<TypedEntity<OtherReadList, OtherWriteList>>{});
}
};
I used std::type_identity just as a type-wrapper which doesn't actually have any data members or logic. If not compiling with C++20 support, any dummy template struct would do, or a raw pointer with null argument.
If using a version of MSVC or a /std: switch which does not support concepts, the concepts can be converted to SFINAE tricks or simple static_asserts.
I'm seeing some inconsistencies between gcc, clang and msvc 2015/2017. The following fails on msvc but not gcc or clang. These meta-functions should return the type of a parameter in a function signature type:
template <int _ParamNum, typename _Ty> struct _get_parameter;
template <typename _ReturnT, typename _HeadT, typename ... _TailT>
struct _get_parameter<0, _ReturnT(_HeadT, _TailT...)>
{
using type = _HeadT;
};
template <int _ParamNum, typename _ReturnT, typename _HeadT, typename ... _TailT>
struct _get_parameter<_ParamNum, _ReturnT(_HeadT, _TailT...)>
{
using type =
typename _::template _get_parameter<_ParamNum - 1, _ReturnT(_TailT...)>::type;
};
called with something like:
using param_type = typename _get_parameter<0, decltype(printf)>::type;
I'm inclined to think its an MSVC bug but I hope somebody can eyeball a problem with the code instead.
Thanks in advance
Edit:
Well I stand corrected: http://rextester.com/XZEL74804 it seems to be some strange interaction with the larger code base.
Consider the following structs:
//Implementations provided elsewhere
struct A { A(int i, double d, std::string s); /* ... */ };
struct B { B(double d1, double d2); /* ... */ };
I have two conversion classes whose template signatures look like:
TupleAs< A, int, double, std::string > via1 { ... };
ArrayAs< B, double, 2 > via2 { ... };
Predictably, TupleAs converts a triplet of int,double, and std::string values into an object of type A. Similarly, ArrayAs converts a pair of two double values into an object of type B. (And yes, there are reasons why I cannot call the A and B constructors directly.)
Improving the syntax
I would like to change the syntax so I can do the following:
TupleAs< A(int,double,std::string) > via1 { ... };
ArrayAs< B(double,2) > via2 { ... };
which, I think, is more descriptive of a conversion process. The TupleAs template declaration and corresponding partial specialization would look like this:
template <typename T> struct TupleAs;
template <typename T, typename ... Args>
struct TupleAs<T(Args...)> { ... };
Compiler errors
However, if I try to do something similar with the ArrayAs version:
template <typename T> struct ArrayAs;
template <typename T, typename U, unsigned N>
struct ArrayAs<T(U,N)> { ... };
I get the following errors in clang (3.6) when trying to instantiate it (ArrayAs< B(double,2)> test;):
typeAs.cpp:14:22: error: unknown type name 'N'
struct ArrayAs<T(U,N)>{
^
typeAs.cpp:14:10: warning: class template partial specialization contains a template parameter that cannot be deduced; this partial specialization will never be used
struct ArrayAs<T(U,N)>{
^~~~~~~~~~~~~~~
typeAs.cpp:13:45: note: non-deducible template parameter 'N'
template<typename T, typename U, unsigned N>
^
The gcc error diagnostic is a little different, but I won't post it here.
I admit that my templating skills should be better than they are, and I also concede that an analogous std::function<B(double,2)> declaration clearly is nonsense. But can someone tell me why the particular syntax I'm trying to achieve is not allowed? I looked through the C++14 standard and had trouble finding the relevant portion, and I'm having trouble interpreting the clang diagnostic message.
When you specialize TupleAs:
template <typename T, typename ... Args>
struct TupleAs<T(Args...)>
You are basically overloading the notation for a function. You are specializing on a function that takes Args... and returns a T. That is a type. You may not be using that function as a function, or really ever think about it as being a type, but that is what it is.
On the other hand, here:
template <typename T, typename U, unsigned N>
struct ArrayAs<T(U,N)> { ... };
There is no such thing as a function that takes N. It could take unsigned, but it can't take a value. There is just no such reasonable thing. From your example, B(double, 2) simply does not make sense. At best, you could write something that would allow:
template <unsigned N> using size_ = std::integral_constant<size_t, N>;
ArrayAs< B(double,size_<2>) >
Or even:
ArrayAs< B(std::array<double, 2>) >
since now we're back to using types everywhere. Whether you prefer that or not is personal preference.
The key here is that types are first-class citizens when it comes to all things template metaprogramming, and values should be avoided where possible.
template <typename T> struct ArrayAs;
template <typename T, typename U, std::size_t N>
struct ArrayAs<T(std::array<U,N>)> { ... };
works, as would:
template<class T>
struct to_array;
template<class T, size_t N>
struct to_array< T[N] > { using type = std::array<T, N>; };
template<class T>
using arr = typename to_array<T>::type;
then:
ArrayAs< Bob( arr<int[3]> ) > some_var;
live example.
Sadly, directly using ArrayAs< Bob( int[3] ) > doesn't work due to how arrays in function types decay to pointers.
I'm attempting variadic template meta programming for the first time and consistently getting a compiler error that I have not been able to track down.
I'm following the "tuple" example on this page (although I'm calling my object an ItemSet)
The ItemSet part compiles just fine:
template<typename...Ts> class ItemSet { };
template<typename item_T, typename ...Ts>
class ItemSet<item_T, Ts...> : public ItemSet<Ts...>
{
public:
ItemSet(item_T t, Ts... item_types) : ItemSet<Ts...>(item_types...), tail(t) { }
protected:
item_T tail;
};
template <typename...M> struct type_holder;
template<typename T, typename ...M>
struct type_holder<0, ItemSet<T, M...>>
{ // ERROR: M parameter pack expects a type template argument.
typedef T type;
};
template <int k, typename T, typename ...M>
struct type_holder<k, ItemSet<T, M...>>
{
typedef typename type_holder<k - 1, ItemSet<M...>>::type type;
};
int main()
{
ItemSet<int, string, string, double> person(0, "Aaron", "Belenky", 29.492);
}
However, in the commented out code, I get the compiler error at the declaration of type_holder.
I've tried a bunch of variations on the same syntax, but always with the same issue.
I'm using Microsoft Visual Studio 2013, which is supposed to have full support for Template programming and Variadic Templates.
Do you understand what the compiler error is, and can you explain it to me?
The immediate problem is that you are defining a specialization of type_holder without a general template. In addition, there is a simple typo (typeholder instead of type_holder). Fixing these two issues makes it compile with other compilers:
template <int, typename T>
struct type_holder;
template <int k, typename T, typename ...M>
struct type_holder<k, ItemSet<T, M...>>
{
typedef typename type_holder<k - 1, ItemSet<M...>>::type type;
};
template<class T, class ...M>
struct type_holder<0, ItemSet<T, M...>>
{
typedef T type;
};
The error emitted by the compilers you used isn't particular helpful. I'd recommend keeping a few C++ compilers around just to test template code with (I'm typically using gcc, clang, and Intel's compiler).
I would like to make a type trait for checking if a particular type is hashable using the default instantiations of the standard library's unordered containers, thus if it has a valid specialization for std::hash. I think this would be a very useful feature (e.g. for using std::set as failsafe for std::unordered_set in generic code). So I, thinking std::hash is not defined for each type, started making the following SFINAE solution:
template<typename T> std::true_type hashable_helper(
const T&, const typename std::hash<T>::argument_type* = nullptr);
template<typename T> std::false_type hashable_helper(...);
//It won't let me derive from decltype directly, why?
template<typename T> struct is_hashable
: std::is_same<decltype(hashable_helper<T>(std::declval<T>())),
std::true_type> {};
(Forgive my modest SFINAE-abilities if this is not the best solution or even wrong.)
But then I learned, that both gcc 4.7 and VC++ 2012 define std::hash for any type T, just static_asserting in the non-specialized version. But instead of compiling conditionally they (and also clang 3.1 using gcc 4.7's libstdc++) fail the assertion resulting in a compile error. This seems reasonable since I think static_asserts are not handled by SFINAE (right?), so an SFINAE solution seems not possibly at all. It's even worse for gcc 4.6 which doesn't even have a static_assert in the general std::hash template but just doesn't define its () operator, resulting in a linker error when trying to use it (which is always worse than a compile error and I cannot imagine any way to transform a linker error into a compiler error).
So is there any standard-conformant and portable way to define such a type trait returning if a type has a valid std::hash specialization, or maybe at least for the libraries static_asserting in the general template (somehow transforming the static_assert error into a SFINAE non-error)?
Since C++17 it is now possible to do this in a more elegant way.
From cppreference about std::hash:
Each specialization of this template is either enabled ("untainted") or disabled ("poisoned"). For every type Key for which neither the library nor the user provides an enabled specialization std::hash, that specialization exists and is disabled. Disabled specializations do not satisfy Hash, do not satisfy FunctionObject, and std::is_default_constructible_v, std::is_copy_constructible_v, std::is_move_constructible_v, std::is_copy_assignable_v, std::is_move_assignable_v are all false. In other words, they exist, but cannot be used.
This meant that the STL had to remove the static_assert in C++17. Here is a working solution with 'Clang-6.0.0 -std=c++17':
#include <functional>
#include <ios>
#include <iostream>
#include <type_traits>
template <typename T, typename = std::void_t<>>
struct is_std_hashable : std::false_type { };
template <typename T>
struct is_std_hashable<T, std::void_t<decltype(std::declval<std::hash<T>>()(std::declval<T>()))>> : std::true_type { };
template <typename T>
constexpr bool is_std_hashable_v = is_std_hashable<T>::value;
struct NotHashable {};
int main()
{
std::cout << std::boolalpha;
std::cout << is_std_hashable_v<int> << std::endl;
std::cout << is_std_hashable_v<NotHashable> << std::endl;
return 0;
}
This might for example come in handy when you use boost::hash_combine or boost::hash_range. If you include a header containing the following code sample you do not need to define boost hashes for specific types anymore.
#include <boost/functional/hash_fwd.hpp>
template <typename T, typename = std::void_t<>>
struct is_boost_hashable : std::false_type { };
template <typename T>
struct is_boost_hashable<T, std::void_t<decltype(boost::hash_value(std::declval<T>()))>> : std::true_type { };
template <typename T>
constexpr bool is_boost_hashable_v = is_boost_hashable<T>::value;
namespace boost
{
template <typename T>
auto hash_value(const T &arg) -> std::enable_if_t<is_std_hashable_v<T> &&
!is_boost_hashable_v<T>, std::size_t>
{
return std::hash<T>{}(arg);
}
}
Notice the is_boost_hashable_v, this is necessary to avoid ambiguity as boost already provides hashes for a lot of hashes.
It seems we have two conflicting requirements:
SFINAE is meant to avoid any instantiation of a template if the instantiation might fail and remove the corresponding function from the overload set.
static_assert() is meant to create an error, e.g., during instantiation of a template.
To my mind, 1. clearly trumps 2., i.e., your SFINAE should work. From the looks of two separate compiler vendors disagree, unfortunately not between themselves but with me. The standard doesn't seem to specify how the default definition of std::hash<T> looks like and seems to impose constraints only for the cases where std::hash<T> is specialized for a type T.
I think your proposed type traits is a reasonable idea and it should be supported. However, it seems the standard doesn't guarantee that it can be implemented. It may be worth bringing this up with the compiler vendors and/or filing a defect report for the standard: The current specification doesn't give clear guidance what should happen, as far as I can tell. ... and if the specification currently mandates that a type traits as above fails it may be a design error which needs to be corrected.
Here is a VERY dirty solution to your problem: It works for GCC 4.7 (and not 4.6, due to missing C++11 feature: mangling overload)
// is_hashable.h
namespace std {
template <class T>
struct hash {
typedef int not_hashable;
};
}
#define hash hash_
#define _Hash_impl _Hash_impl_
#include<functional>
#undef hash
#undef _Hash_impl
namespace std {
struct _Hash_impl: public std::_Hash_impl_{
template <typename... Args>
static auto hash(Args&&... args)
-> decltype(hash_(std::forward<Args>(args)...)) {
return hash_(std::forward<Args>(args)...);
}
};
template<> struct hash<bool>: public hash_<bool> {};
// do this exhaustively for all the hashed standard types listed in:
// http://en.cppreference.com/w/cpp/utility/hash
}
template <typename T>
class is_hashable
{
typedef char one;
typedef long two;
template <typename C> static one test( typename std::hash<C>::not_hashable ) ;
template <typename C> static two test(...);
public:
enum { value = sizeof(test<T>(0)) == sizeof(long) };
};
// main.cpp
// #include "is_hashable.h"
#include<iostream>
#include<unordered_set>
class C {};
class D {
public:
bool operator== (const D & other) const {return true;}
};
namespace std {
template <> struct hash<D> {
size_t operator()(const D & d) const { return 0;}
};
}
int main() {
std::unordered_set<bool> boolset;
boolset.insert(true);
std::unordered_set<D> dset;
dset.insert(D());// so the hash table functions
std::cout<<is_hashable<bool>::value<<", ";
std::cout<<is_hashable<C>::value << ", ";
std::cout<<is_hashable<D>::value << "\n";
}
And the output is:
1, 0, 1
We basically "hijack" the hash symbol and inject some helper typedef in it. You'll need to modify it for VC++, in particular, the fix for _Hash_impl::hash() since it's an implementation detail.
If you make sure that the section labelled as is_hashable.h is included as the first include this dirty trick should work...
I hit this too. I tried a few workarounds and went with a whitelist filter for std::hash<>. the whitelist is not pleasant to maintain, but it is safe and it works.
I tried this on VS 2013, 2015, clang and gcc.
#include <iostream>
#include <type_traits>
// based on Walter Brown's void_t proposal
// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3911.pdf
namespace detail {
template<class... TN> struct void_t {typedef void type;};
}
template<class... TN>
struct void_t {typedef typename detail::void_t<TN...>::type type;};
// extensible whitelist for std::hash<>
template <class T, typename = void>
struct filtered_hash;
template <class T>
struct filtered_hash<T,
typename std::enable_if<std::is_enum<T>::value>::type>
: std::hash<T> {
};
template <class T>
struct filtered_hash<T,
typename std::enable_if<std::is_integral<T>::value>::type>
: std::hash<T> {
};
template <class T>
struct filtered_hash<T,
typename std::enable_if<std::is_pointer<T>::value>::type>
: std::hash<T> {
};
template<typename, typename = void>
struct is_hashable
: std::false_type {};
template<typename T>
struct is_hashable<T,
typename void_t<
typename filtered_hash<T>::result_type,
typename filtered_hash<T>::argument_type,
typename std::result_of<filtered_hash<T>(T)>::type>::type>
: std::true_type {};
// try it out..
struct NotHashable {};
static_assert(is_hashable<int>::value, "int not hashable?!");
static_assert(!is_hashable<NotHashable>::value, "NotHashable hashable?!");
int main()
{
std::cout << "Hello, world!\n";
}