I am trying to define a simple variant-based Result type alias, sort of like a poor man's rust-like Result type
:
namespace detail {
template <typename SuccessType, typename... ErrorTypes>
struct Result {
using type = std::variant<SuccessType, ErrorTypes...>;
};
template <typename... ErrorTypes>
struct Result<void, ErrorTypes...> {
using type = std::variant<std::monostate, ErrorTypes...>;
};
} // namespace detail
template <typename SuccessType, typename... ErrorTypes>
using Result_t = detail::Result<SuccessType, ErrorTypes...>::type;
i.e. a Result_t is just an std::variant where the 0th index is the successful result and the rest are error structs.
I defined this helper method to check if the result is good:
template <typename SuccessType, typename... ErrorTypes>
inline bool Ok(const Result_t<SuccessType, ErrorTypes...>& r) {
return r.index() == 0;
}
But I get a "no matching overloaded function found" when I try to instantiate it:
error C2672: 'Ok': no matching overloaded function found
error C2783: 'bool Ok(const detail::Result<SuccessType,ErrorTypes...>::type &)': could not deduce template argument for 'SuccessType'
struct FileError {};
struct BadJson {};
template <typename T>
using Result = Result_t<T, FileError, BadJson>;
Result<void> GetVoid() { return {}; }
TEST(ConfigFileTest, Result) {
auto res = GetVoid();
EXPECT_EQ(res.index(), 0);
bool ok = Ok(res);
EXPECT_TRUE(ok);
}
What am I doing wrong? If I just have Ok be templated like template <typename T> Ok(const T& r) it works, but makes the function too general.
After expanding the Result_t alias in the function parameter, it looks like this:
template <typename SuccessType, typename... ErrorTypes>
bool Ok(const detail::Result<SuccessType, ErrorTypes...>::type& r) {
return r.index() == 0;
}
The problematic part here is that the template parameters are left of the name resolution operator ::. Everything left of :: is a non-deduced context, meaning that it is not used to deduce template arguments. So since SuccessType and ErrorTypes... appear only in non-deduced context, they cannot be deduced and a call which doesn't explicitly specifies them will fail.
You can see that this rule is necessary, because theoretically any specialization of detail::Result<SuccessType, ErrorTypes...> could have a ::type that matches the arguments type. There is no way that the compiler can check this for every possible combination of types.
Instead of trying to alias types, make Result an actual new type:
template <typename SuccessType, typename... ErrorTypes>
struct Result {
using variant_type = std::variant<SuccessType, ErrorTypes...>;
variant_type variant;
};
template <typename... ErrorTypes>
struct Result<void, ErrorTypes...> {
using variant_type = std::variant<std::monostate, ErrorTypes...>;
variant_type variant;
};
template <typename SuccessType, typename... ErrorTypes>
bool Ok(const Result<SuccessType, ErrorTypes...>& r) {
return r.variant.index() == 0;
}
or something along those lines. If you really want to use the old design using only aliases, then the function should not take the nested alias as argument, but the actual type instead (which is probably not match the intent of the design):
template <typename T, typename... ErrorTypes>
bool Ok(const std::variant<T, ErrorTypes...>& r) {
return r.index() == 0;
}
(I removed the inline on the templates. inline on a function template doesn't really make much sense.)
Related
I'm having a hard time understanding how to stop code from being evaluated with std::conditional_t in the false branch.
#include <type_traits>
using namespace std;
namespace {
template <typename T>
using BaseDifferenceType = decltype (T{} - T{});
}
int main ()
{
using T = int;
static_assert(! is_enum_v<T>);
BaseDifferenceType<T> xxx {};
// PROBLEM IS HERE - though is_enum is false, we still evaluate underlying_type<T> and complain it fails
using aaa = conditional_t<is_enum_v<T>, underlying_type_t<T>, BaseDifferenceType<T>>;
return 0;
}
You can try this online at https://www.onlinegdb.com/uxlpSWVXr.
Compiling (with C++17) gives the error:
error: ‘int’ is not an enumeration type
typedef __underlying_type(_Tp) type;
^~~~ main.cpp: In function ‘int main()’: main.cpp:16:87: error: template argument 2 is invalid
using aaa = conditional_t<is_enum_v<T>, underlying_type_t<T>, BaseDifferenceType<T>>;
The answer is simple:
You don't.
std::conditional_t always has three fully evaluated arguments:
Something boolean-like, and two types.
If one of them cannot be evaluated if the other is selected, you need to use a custom template and specialize it appropriately:
template <class T, bool = std::is_enum_v<T>>
struct type_finder { using type = std::underlying_type_t<T>; };
template <class T>
struct type_finder<T, false> { using type = BaseDifferenceType<T>; };
template <class T>
using type_finder_t = typename type_finder<T>::type;
An alternative is using if constexpr and automatic return-type-deduction:
template <class T>
auto type_finder_f() {
if constexpr (std::is_enum_v<T>)
return std::underlying_type_t<T>();
else
return BaseDifferenceType<T>();
}
template <class T>
using type_finder_t = decltype(type_finder_f<T>());
A possible alternative pass through a creation of a custom type traits as follows
template <template <typename...> class Cont, typename ... Ts>
struct LazyType
{ using type = Cont<Ts...>; };
and rewrite your std::conditional as follows
using aaa = std::conditional_t<
std::is_enum_v<T>,
LazyType<underlying_type_t, T>,
LazyType<BaseDifferenceType, T>>::type;
// ............................................^^^^^^
Observe I've added a final ::type: it's the "constructor" of the required type, completely avoiding the unwanted type.
Off Topic Unrequested Suggestion: avoid
using namespace std;
It's considered bad practice
Let's say I have something, following the example on cppreference.com, but specified differently:
typedef double call_t(char, int&);
I need such a form of invocation of result_of that would give back the return type of the function signature defined as the above call_t. That is something that I could use as:
template <class Signature>
std::result_of<SOME_MAGIC_AROUND(Signature)>::type call(Signature* f)
{
return (*f)();
}
double d = call(fn); // where `fn` is declared as having `call_t` type
Doing result_of<call_t>::type doesn't work. I've tried also various combinations around it, but came to nothing.
You have to specify the parameters too. e.g.
template <class Signature>
typename std::result_of<Signature*(char, int&)>::type call(Signature* f)
// ^^^^^^^^^^^^^^^^^^^^^^
{
return (*f)(...);
}
LIVE
std::result_of has been deprecated since C++17, you can use std::invoke_result instead.
template <class Signature>
typename std::invoke_result<Signature*, char, int&>::type call(Signature* f)
// ^^^^^^^^^^^^^^^^^^^^^^
{
return (*f)(...);
}
EDIT
Signature is a template parameter, but expected to be a typedef for a function. I need its "return type" so that I can use as a return type for the call forwarder - regardless of what parameters a user might have specified for Signature.
You can make a type trait which gets the return type from the function type. e.g.
template <typename F>
struct return_type_of_function {};
template <typename R, typename... Args>
struct return_type_of_function<R(Args...)> {
using type = R;
};
then use it as
template <class Signature>
typename return_type_of_function<Signature>::type call(Signature* f)
{
return (*f)(...);
}
LIVE
Here is my code:
template<
template <typename TSEvent,
typename ...TSEvents> typename V,
typename... Filtered>
constexpr auto filter() {
if constexpr(sizeof...(TSEvents) == 0) {
return type_list<Filtered...>{};
}
if constexpr(is_default_constructible<TSEvent>::value) {
return filter<<TSEvents...>, Filtered...>();
}
return filter<<TSEvents...>, Filtered...>();
}
I however get this error, size...(TSEvents), TSEvents is not declared. Is there anyway for me to access TSEvents in my nested template?
Usually through another level of indirection, and usually a struct that we can specialize.
For example:
namespace detail
{
template<class...>
struct filter_t;
template<template<class, class...> class V, class TSEvent, class... TSEvents, class... Filtered>
struct filter_t<V<TSEvent,TSEvents...>, Filtered...>
{
static constexpr auto filter() {
return sizeof...(TSEvents);
}
};
} // detail
template<class... T>
constexpr auto filter()
{
return detail::filter_t<T...>::filter();
}
template<class T, class...U>
struct type_list{};
int main()
{
std::cout << filter<type_list<int, int, int>, int>();
}
Live Demo
Just to present another option, you could do this with only functions.
#include <iostream>
using namespace std;
template<typename...>
struct type_list{};
template < template <typename...> typename T,typename A,typename... B, typename... Filtered>
constexpr auto filter_impl(T<A,B...>*,type_list<Filtered...>)
{
using filtered_list = std::conditional_t<is_arithmetic<A>::value,
type_list<Filtered...,A>,
type_list<Filtered...>>;
if constexpr (sizeof...(B) == 0)
return filtered_list();
else
return filter_impl( (T<B...>*)0, filtered_list());
}
template <typename T>
constexpr auto filter()
{
return filter_impl( (T*)0,type_list<>());
}
struct not_arethmetic{};
int main() {
auto b = filter< type_list<not_arethmetic,int,bool,not_arethmetic,double> >();
static_assert(std::is_same< decltype(b) , type_list<int,bool,double>>::value);
return 0;
}
Demo
One thing, In your original example your first if expression will mean that the final TSEvent is not checked, as it returns if the varadic TSEvents... is zero size, but there will be one final element to check whether is_default_constructible.
Also, you might find this post useful regarding template template parameter names.
I however get this error, size...(TSEvents), TSEvents is not declared. Is there anyway for me to access TSEvents in my nested template?
Short answer: no.
Long answer: with
template<
template <typename TSEvent,
typename ...TSEvents> typename V,
typename... Filtered>
constexpr auto filter()
you set two template arguments for the filter() function.
The first one, related to the TSEvents variadic list, is a template-template argument that receive one or more types argument.
But your function doesn't receive a type that is based over that template-template (with a fixed TSEvent type and a fixed TSEvents); receive the template-template.
So doesn't make sense the test size...(TSEvents) because, for filter() isn't
fixed the TSEvents list.
To explain this in another way... you can call filter this way
filter<std::tuple, short, int, long>();
Ask for sizeof...(TSEvents) is asking how many types contains std::tuple where std::tuple is only the container of types but without contained types.
If you want to make some sort of actions in your filter() function, you need a type template parameter, not a template-template parameter.
It's simpler with classes (see AndyG's answer) where you can use partial specialization (with functions you can't) or with function when they receive arguments from which you can deduce types.
Suppose your filter() receive an object of type V<SomeTypes...> and an object of type std::tuple<Filtered...>, you can write something as follows (caution: code not tested)
template<
template <typename ...> typename V,
typename TSEvent, typename ... TSEvents, typename... Filtered>
constexpr auto filter (V<TSEvent, TSEvents...> const & v,
std::tuple<Filtered...> const & t) {
/* some code where you can use also TSEvent and TSEvents... */
}
This way TSEvent and TSEvents... are deduced from the v argument.
I would like to define a class, with an optional template parameter such that:
if the parameter exists and it is an enum class, defines an
operator()
the class can be istantiated without the additional template
parameter (in which case operator() does not exists)
The goal is to allow access to an array element through an optional enum class.
Code:
#include <type_traits>
template <int N, typename... T>
class data {
int x[N];
template <typename T0, typename... T1>
struct enum_wrapper {
typedef T0 type;
};
public:
template <bool activate = (sizeof...(T) > 0)>
std::enable_if_t<activate, int>&
operator()(const typename enum_wrapper<T...>::type& e)
{ return x[static_cast<std::size_t>(e)]; }
};
int main()
{
data<3> a;
return 0;
}
The purpose of enum_wrapper is to single out the first parameter in the pack typename... T.
The code compiled with g++ -std=c++14 gives an error
error: wrong number of template arguments (0, should be at least 1)
on the definition of operator(). This is because enum_wrapper<T...>::type is not defined when data is istantiated with a single parameter <3>.
However, the template parameter bool activate in the definition of operator() and its return type std::enable_if_t<activate, int> is meant to prevent the instantation of operator() when the parameter pack typename... T is empty.
Why the definition of operator() is not simply discarded under SFINAE?
Also, I would like to further eliminate operator() when enum_wrapper::type is not an enum class, using std::is_enum.
Where a condition like
std::enable_if<std::is_enum_v<(enum_wrapper<T...>::type)>>
should be inserted to make SFINAE work?
The issue is that the argument type to operator() doesn't depend on the template arguments (to operator())
You can make it depend via an indirection that specifies a new parameter pack that must be the same as the class' (unfortunately we cannot default a parameter pack otherwise we would)
template <bool activate = (sizeof...(T) > 0), typename... U>
std::enable_if_t<activate && std::conjunction_v<std::is_same<T, U>...>, int>&
operator()(const typename enum_wrapper<U...>::type& e)
{ return x[static_cast<std::size_t>(e)]; }
Demo
I suppose I have some concerns as to how'd you actually plan on calling operator(), since enum_wrapper is private within the class. I think what you're really interested in is receiving a type that is the same as enum_wrapper<T...>::type. What is easiest here is to simply make a specialization for enum_wrapper for an empty T..., and then disable operator() for it:
template<class...>
struct enum_wrapper{using type = int;};
template <typename T0, typename... T1>
struct enum_wrapper<T0, T1...> {
typedef T0 type;
};
//...
template <bool activate = (sizeof...(T) > 0)>
std::enable_if_t<activate, int>&
operator()(const typename enum_wrapper<T...>::type& e)
{ return x[static_cast<std::size_t>(e)]; }
And then we could call it like so:
data<3> a; // no operator()
data<3, int> b; // operator() viable
b.x[0] = 1;
b.x[1] = 3;
b.x[2] = 5;
std::cout << b(1) << std::endl; // print '3'
Better Demo
(I made member x public for testing purposes)
In the end, it may be easier to simply move the condition that your argument to operator() is of the type enum_wrapper<T...>::type into a static_assert within the function that is protected by your bool activate:
template <class U, bool activate = (sizeof...(T) > 0)>
std::enable_if_t<activate, int>&
operator()(const U& e)
{
static_assert(std::is_same_v<U, typename enum_wrapper<T...>::type>, L"argument type to operator() is incorrect");
return x[static_cast<std::size_t>(e)];
}
You can call it like before, and there's still no operator() defined for a. If you tried to call b(2.0), though, you'd trigger the static assertion.
Best Demo
I was experimenting with SFINAE these days, and something puzzles me. Why my_type_a cannot be deduced in my_function's instantiation?
class my_type_a {};
template <typename T>
class my_common_type {
public:
constexpr static const bool valid = false;
};
template <>
class my_common_type<my_type_a> {
public:
constexpr static const bool valid = true;
using type = my_type_a;
};
template <typename T> using my_common_type_t = typename my_common_type<T>::type;
template <typename T, typename V>
void my_function(my_common_type_t<T> my_cvalue, V my_value) {}
int main(void) {
my_function(my_type_a(), 1.0);
}
G++ gives me this:
/home/flisboac/test-template-template-arg-subst.cpp: In function ‘int main()’:
/home/flisboac/test-template-template-arg-subst.cpp:21:30: error: no matching function for call to ‘my_function(my_type_a, double)’
my_function(my_type_a(), 1.0);
^
/home/flisboac/test-template-template-arg-subst.cpp:18:6: note: candidate: template<class T, class V> void my_function(my_common_type_t<T>, V)
void my_function(my_common_type_t<T> my_type, V my_value) {}
^~~~~~~~~~~
/home/flisboac/test-template-template-arg-subst.cpp:18:6: note: template argument deduction/substitution failed:
/home/flisboac/test-template-template-arg-subst.cpp:21:30: note: couldn't deduce template parameter ‘T’
my_function(my_type_a(), 1.0);
^
What I expected was that, when calling my_function as I did in main, T would be deduced to the type of the function's first argument, and that type would be used in the function's instantiation. But it seems that my_common_type_t<T> is instantiated before the function, but even then, the type of my_cvalue would become my_type_a anyways, so I cannot see why this wouldn't work...
Is there a different way to do this? Should I just avoid two (or more) levels of template indirection?
Well, consider this:
template <>
struct my_common_type<int> {
constexpr static const bool valid = true;
using type = my_type_a;
};
template <>
struct my_common_type<double> {
constexpr static const bool valid = true;
using type = my_type_a;
};
// ...
int main(void) {
my_function(my_type_a{}, 1.0);
}
Does the compiler chooses my_common_type<int> or my_common_type<double>?
If the language would permit deduction in you case, it would have to match what T would be in my_common_type<T>::type in order to yield the exact type you send to the function parameter. Obviously, it's not only impossible, but with my example above, it may have multiple choices!
Fortunately, there is a way to tell the compiler that my_common_type<T> will always yield to T. The basics of the trick is this:
template<typename T>
using test_t = T;
template<typename T>
void call(test_t<T>) {}
int main() {
call(1);
}
What is T deduces to? int, easy! The compiler is happy with this kind of match. Also, since test_t cannot be specialized, test_t<soxething> is known to only be something.
Also, this is working too with multiple levels of aliases:
template<typename T>
using test_t = T;
template<typename T>
using test2_t = test_t<T>;
template<typename T>
void call(test2_t<T>) {}
int main() {
call(1); // will also work
}
We can apply this to your case, but we will need some tool:
template<typename T, typename...>
using first_t = T;
This is the same easy match as above, but we can also send some argument that will not be used. We will make sfinae in this unused pack.
Now, rewrite my_common_type_t to still be an easy match, whilst adding the constraint in the unused pack:
template <typename T>
using my_common_type_t = first_t<T, typename my_common_type<T>::type>;
Note that this is also working:
template <typename T>
using my_common_type_t = first_t<T, std::enable_if_t<my_common_type<T>::valid>>;
Now deduction will happen as expected! Live (GCC) Live (Clang)
Note that this trick will only work with C++14, as sfinae in this case (dropped parameters) is only guaranteed to happen since C++14.
Also note that you should either use struct for your trait, or use public: to make the member my_common_type<T>::type public, or else GCC will output a bogus error.