I'm trying to write syntactic sugar, in a monad-style, over std::optional. Please consider:
template<class T>
void f(std::optional<T>)
{}
As is, this function cannot be called with a non-optional T1 (e.g. an int), even though there exists a conversion from T to std::optional<T>2.
Is there a way to make f accept an std::optional<T> or a T (converted to an optional at the caller site), without defining an overload3?
1) f(0): error: no matching function for call to 'f(int)' and note: template argument deduction/substitution failed, (demo).
2) Because template argument deduction doesn't consider conversions.
3) Overloading is an acceptable solution for a unary function, but starts to be an annoyance when you have binary functions like operator+(optional, optional), and is a pain for ternary, 4-ary, etc. functions.
Another version. This one doesn't involve anything:
template <typename T>
void f(T&& t) {
std::optional opt = std::forward<T>(t);
}
Class template argument deduction already does the right thing here. If t is an optional, the copy deduction candidate will be preferred and we get the same type back. Otherwise, we wrap it.
Instead of taking optional as argument take deductible template parameter:
template<class T>
struct is_optional : std::false_type{};
template<class T>
struct is_optional<std::optional<T>> : std::true_type{};
template<class T, class = std::enable_if_t<is_optional<std::decay_t<T>>::value>>
constexpr decltype(auto) to_optional(T &&val){
return std::forward<T>(val);
}
template<class T, class = std::enable_if_t<!is_optional<std::decay_t<T>>::value>>
constexpr std::optional<std::decay_t<T>> to_optional(T &&val){
return { std::forward<T>(val) };
}
template<class T>
void f(T &&t){
auto opt = to_optional(std::forward<T>(t));
}
int main() {
f(1);
f(std::optional<int>(1));
}
Live example
This uses one of my favorite type traits, which can check any all-type template against a type to see if it's the template for it.
#include <iostream>
#include <type_traits>
#include <optional>
template<template<class...> class tmpl, typename T>
struct x_is_template_for : public std::false_type {};
template<template<class...> class tmpl, class... Args>
struct x_is_template_for<tmpl, tmpl<Args...>> : public std::true_type {};
template<template<class...> class tmpl, typename... Ts>
using is_template_for = std::conjunction<x_is_template_for<tmpl, std::decay_t<Ts>>...>;
template<template<class...> class tmpl, typename... Ts>
constexpr bool is_template_for_v = is_template_for<tmpl, Ts...>::value;
template <typename T>
void f(T && t) {
auto optional_t = [&]{
if constexpr (is_template_for_v<std::optional, T>) {
return t;
} else {
return std::optional<std::remove_reference_t<T>>(std::forward<T>(t));
}
}();
(void)optional_t;
}
int main() {
int i = 5;
std::optional<int> oi{5};
f(i);
f(oi);
}
https://godbolt.org/z/HXgoEE
Another version. This one doesn't involve writing traits:
template <typename T>
struct make_optional_t {
template <typename U>
auto operator()(U&& u) const {
return std::optional<T>(std::forward<U>(u));
}
};
template <typename T>
struct make_optional_t<std::optional<T>> {
template <typename U>
auto operator()(U&& u) const {
return std::forward<U>(u);
}
};
template <typename T>
inline make_optional_t<std::decay_t<T>> make_optional;
template <typename T>
void f(T&& t){
auto opt = make_optional<T>(std::forward<T>(t));
}
Related
Consider the following class:
// Class definition
template <template <class...> class... Templates>
class template_pack
{
public:
template <class... Types>
constexpr template_pack(const Types&...) noexcept;
};
// Class template argument deduction guide
template <class... Types>
template_pack(const Types&...) -> template_pack</* something here */>
We suppose that the Types... are of the form template <class...> class... Templates. What I would like is:
template_pack pack(std::vector<int>{}, std::list<double>{}, std::deque<char>{});
to lead to:
template_pack<std::vector, std::list, std::deque>;
How to make that work?
How to make that work?
I don't see a way: there is ever something that can't be deduced.
Not exactly what you asked but the best I can imagine pass trough a custom type-traits ttw (for "template-template-wrapper")
template <template <typename...> class C>
struct ttw
{
template <typename ... Ts>
constexpr ttw (C<Ts...> const &)
{ }
};
that, using implicit deduction guides, extracts the template-template from the type received from constructor and use it as template parameter.
So you can write template_pack with a constructor that receives ttw<Templates>
template <template <typename...> class... Templates>
struct template_pack
{
constexpr template_pack (ttw<Templates> const & ...)
{ }
};
that you can use as follows (again: trough implicit deduction guides)
template_pack tp1 {ttw{std::vector<int>{}},
ttw{std::set<long>{}},
ttw{std::map<char, short>{}}};
The problem is that it's necessary explicitly wrap the arguments in ttw{} because, to make an example, a std::vector<int> is convertible to a ttw<std::vector> but isn't a ttw<std::vector>. So, passing std::vector{} instead of ttw{std::vector{}}, we have the usual chicken/egg problem of a type that can't be deduced because, to deduce it, is needed a conversion that require to knowledge of type that we want deduce.
Obviously, you can demand the explicit ttw wrapping works to a specific make_template_pack() function
template <typename ... Ts>
constexpr auto make_template_pack (Ts && ... ts)
{ return template_pack{ttw{std::forward<Ts>(ts)}...}; }
The following is a full compiling example
#include <map>
#include <set>
#include <vector>
#include <type_traits>
template <template <typename...> class C>
struct ttw
{
template <typename ... Ts>
constexpr ttw (C<Ts...> const &)
{ }
};
template <template <typename...> class... Templates>
struct template_pack
{
constexpr template_pack (ttw<Templates> const & ...)
{ }
};
template <typename ... Ts>
constexpr auto make_template_pack (Ts && ... ts)
{ return template_pack{ttw{std::forward<Ts>(ts)}...}; }
int main ()
{
template_pack tp1 {ttw{std::vector<int>{}},
ttw{std::set<long>{}},
ttw{std::map<char, short>{}}};
auto tp2 { make_template_pack(std::vector<long>{},
std::set<int>{},
std::map<char, short>{}) };
using t0 = template_pack<std::vector, std::set, std::map>;
using t1 = decltype(tp1);
using t2 = decltype(tp2);
static_assert( std::is_same<t0, t1>::value );
static_assert( std::is_same<t0, t2>::value );
}
I "succeed" with additional traits:
template <typename T> struct template_traits;
// Variadic case
template <template <class...> class C, typename ... Ts>
struct template_traits<C<Ts...>>
{
template <typename ... Us>
using template_type = C<Us...>;
};
And then, argument deduction is:
// Class template argument deduction guide
template <class... Types>
template_pack(Types&&...)
-> template_pack<template_traits<std::decay_t<Types>>::template template_type...>;
Demo
Issue is that aliases are not really identical (gcc considers some aliases as identical
contrary to clang) (I opened a question for that BTW)
template_traits<std::vector>::template_type is not std::vector even if for any T, A, template_traits<std::vector>::template_type<T, A> is not std::vector<T, A>.
There's a shortcut you can take if each template has only one argument:
template <template<class> class... Templates, class... Types>
template_pack(const Templates<Types>&...) -> template_pack<Templates...>;
With only one argument each, it's easy to split up one pack amongst all the templates.
Unfortunately, I don't know of any way to have a separate pack per template without knowing the number of templates in advance. Therefore, a layer of indirection through a helper seems required. In addition, deduction guides must be of the form -> template_pack<something>, presumably to avoid having the compiler do too much work or run into impossible problems. Given this, the class needs a slight tweak:
template <template <class...> class... Templates>
class holder {};
// Class definition
template<class Holder>
class template_pack;
template <template <class...> class... Templates>
class template_pack<holder<Templates...>>
{
public:
template <class... Types>
constexpr template_pack(const Types&...) noexcept {}
};
With this tweak, we can make a helper (that could probably be simplified to be a bit more straightforward):
template<template<class...> class... TTs>
struct result {
using type = holder<TTs...>;
};
template<class T>
struct type {};
template<class Prev, class Current, class... Rest>
auto helper() {
return []<template<class...> class... PrevTTs, template<class...> class CurrTT, class... CurrTs>(result<PrevTTs...>, type<CurrTT<CurrTs...>>) {
if constexpr (sizeof...(Rest) == 0) {
return result<PrevTTs..., CurrTT>{};
} else {
return helper<result<PrevTTs..., CurrTT>, Rest...>();
}
}(Prev{}, type<Current>{});
}
I use C++20's templated lambdas to pull apart two templates from their arg packs inline instead of having an additional helper layer, but that additional layer is still possible in earlier standards, just uglier. The helper recursively takes a previous result, pulls apart one template at a time, adds it to the result, and recursively calls itself until there are no arguments left.
With this helper, it becomes possible to make the deduction guide:
// Class template argument deduction guide
template <typename... Ts>
template_pack(const Ts&...) -> template_pack<typename decltype(helper<result<>, Ts...>())::type>;
You can find a full example here. It might also be possible to improve this code somewhat significantly, but the core idea is there.
Something like that seems to be working
#include <iostream>
#include <vector>
#include <list>
#include <deque>
template<typename... TS>
struct Pack;
template<typename S, typename... TS>
struct Pack<S, TS...> {
S s;
Pack<TS...> ts;
static constexpr size_t size = Pack<TS...>::size + 1;
constexpr Pack(S&& s, TS&&... ts) noexcept
: s(s)
, ts(std::forward<TS>(ts)...)
{}
};
template<typename S>
struct Pack<S> {
S s;
static constexpr size_t size = 1;
constexpr Pack(S&& s) noexcept
: s(s)
{}
};
template<>
struct Pack<> {
static constexpr size_t size = 0;
};
template<typename... TS>
constexpr auto make_pack(TS&&... ts) noexcept {
return Pack<TS...>(std::forward<TS>(ts)...);
}
int main() {
auto empty_pack = make_pack();
std::cout << empty_pack.size << std::endl; // 0
auto vector_pack = make_pack(std::vector<int>{});
std::cout << vector_pack.size << std::endl; // 1
auto vector_list_deque_pack = make_pack(std::vector<int>{}, std::list<double>{}, std::deque<char>{});
std::cout << vector_list_deque_pack.size << std::endl; // 3
}
The following code does not work because the inferred template parameter F is std::tuple, whereas I want it to be Foo - the former takes two template parameters and the latter takes one.
#include <tuple>
template <typename T>
using Foo = std::tuple<int, T>;
template <template <typename> class F>
void foo(F<std::string> bar) {}
void test() {
foo(Foo<std::string>());
}
Is there any way to make type inference work with the using statement rather than turning Foo into it's own class?
#include <tuple>
template <typename T>
class Foo {
std::tuple<int, T> bar;
};
template <template <typename> class F>
void foo(F<std::string> bar) {}
void test() {
foo(Foo<std::string>());
}
More Info
I am using C++17's std::variant along with using to alias types that are generic on a single type and I would prefer to declare these with using statements rather than creating wrapper classes for each one. Something like this:
// Assuming Plus, Minus, etc all exist
template <typename T>
using Operation = std::variant<Plus<T>, Minus<T>, Times<T>>;
Building a Haskell-Style Functor
The point of this exercise is to build a small functor library loosely based on Haskell's functor typeclass. I have defined the "typeclass" like this:
template <template <typename> class F>
class Functor {
public:
template <typename T, typename U>
static F<U> fmap(std::function<U(T)> f, F<T> functor);
};
But I also wanted to add some sugar so that you can create a general mapper that will map a function over any function type without pre-specifying the functor type:
template <typename T, typename U>
struct FMap {
FMap(std::function<U(T)> f) : f_(f) {}
template <template <typename> class F>
F<U> operator()(F<T> functor) {
return Functor<F>::fmap(f_, functor);
}
private:
std::function<U(T)> f_;
};
template <typename T, typename U>
FMap<T, U> fmap(std::function<U(T)> f) {
return FMap<T, U>(f);
}
This works well with a simple value-wrapper functor:
template <typename T>
class Value {
public:
Value(T value) : value_(value) {}
const T& value() const {
return value_;
}
private:
T value_;
};
template <>
template <typename T, typename U>
Value<U> Functor<Value>::fmap(std::function<U(T)> f, Value<T> value) {
return Value<U>(f(value.value()));
}
void test() {
std::function<std::string(int)> fn = [](int x) {
return std::to_string(x);
};
auto result = fmap(fn)(Value(42));
// result.value() == "42"
}
Now I am trying to get it to work with a more complicated type that uses std::tuple or std::variant like in the above example.
template <>
template <typename T, typename U>
Foo<U> Functor<Foo>::fmap(std::function<U(T)> f, Foo<T> value) {
return Foo<U>(std::get<0>(value), f(std::get<1>(value)));
}
void test() {
std::function<std::string(int)> fn = [](int x) {
return std::to_string(x);
};
// This is the desirable syntax but it doesn't build
// fmap(fn)(Foo<int>(42, 7));
// This builds but it's super ugly
fmap(fn).operator()<Foo>(Foo<int>(42, 7));
}
Based on the response by SkepticalEmpiricist below, I am thinking that type aliases may not be the way to go here and instead I will have to introduce small wrapper classes - unless there is an SFINAE approach that would get this working.
This library is mostly a curiosity and a means for me to explore some more advanced template concepts - thanks for the help!
So first attempt before we start digging for some SFINAE based trickery to try circumvent the unavoidable:
Alias templates are never deduced by template argument deduction
We could "deduce" the template arguments for the compiler ourselves like this:
#include <tuple>
template <typename T>
using Foo = std::tuple<int, T>;
template <template <typename ...> class F, typename T, typename ...Ts>
void foo(F<T, std::string, Ts...> bar) {}
void test() {
foo(Foo<std::string>());
}
So now we have it compiling for your foo(Foo<std::string>()); call with Foo being the alias template over std::tuple and, more importantly, foo() is still specialized only for Foo<std::string>.
However, to support usage simultaneously of foo() for both the std::tuple alias template and the wrapper class for example, we still don't have it compiling error-free. As in, if we now comment-out the tuple-flavor Foo and bring back in the wrapper class Foo then calling our rewritten foo() will not compile.
To address the issue, let's give it a try with SFINAE to the rescue and replace the last declaration of foo() with this code:
template <template <typename ...> class F, typename T, typename ...Ts,
typename std::enable_if_t<std::is_same<F<T, Ts...>,
std::tuple<T, Ts...>>::value >* = nullptr>
void foo(F<T, std::string, Ts...> bar) {}
template <template <typename> class F>
void foo(F<std::string> bar) {}
Now you can call foo() for instances of both wrapper class of tuples and alias template for tuples. You could implement in the same fashion for std::variant as well.
With:
template <typename T> using Foo = std::tuple<int, T>;
template <template <typename> class F> void foo(F<std::string> bar) {}
void test() { foo(Foo<std::string>()); }
Foo<std::string> is std::tuple<int, std::string>.
so test is
void test() { foo(std::tuple<int, std::string>()); }
How do you expect compiler deduce from tuple from which alias it come ?
we might have
template <typename T> using Bar = std::tuple<int, std::string>;
template <typename T> using Bar2 = std::tuple<some_trait<T>::type, some_trait<T>::type2>;
// ...
A possible workaround might be:
template <typename T, typename U>
Foo<U> fmap(std::function<U(T)> f, Foo<T> value)
{
return Foo<U>(std::get<0>(value), f(std::get<1>(value)));
}
With calling syntax:
fmap(fn, Foo<int>(42, 7));
To determine if a function exists for a function, you can use the following:
template <typename...Ts>
using void_t = void;
void fn(int);
struct X {};
template <typename T, typename = void_t<decltype(fn(std::declval<T>()))>>
void fn2(T) { }
void test() {
fn2(int(1)); // works
//fn2(X()); // doesn't work
}
Now, is there a way of detecting if the fn(T) doesn't exist for type T?
Example:
void test2() {
//fn2(int(1)); // doesn't work
fn2(X()); // works
}
The reason for this is to define an exclusion operation so that I can define fn2() for both to avoid an ambiguity error.
The usual way of doing it is to make a type trait, as #chris said:
template <typename T, typename = void>
struct fn_callable_with : std::false_type {};
template <typename T>
struct fn_callable_with<T, void_t<decltype(fn(std::declval<T>()))>> : std::true_type {};
// For bonus C++14 points:
// template <typename T>
// /*C++17: inline*/ constexpr bool fn_callable_with_v = fn_callable_with<T>::value;
template <typename T, typename = typename std::enable_if<!fn_callable_with<T>::value>::type>
// C++14: template <typename T, typename = std::enable_if_t<!fn_callable_with_v<T>>>
void fn2(T) { }
You need another overload of fn2, otherwise SFINAE won't do anything useful.
void fn2(...) { } // called if the overload below is SFINAEd away
template <typename T, typename = void_t<decltype(fn(std::declval<T>()))>>
void fn2(T) { }
live example on wandbox
The idea is that SFINAE can remove candidates from an overload set. If fn2 is the only candidate, then you're going to get an hard error.
I have a case where I need to apply the input argument to a function without caring if it is a tuple or not. If it is a tuple, it needs to be unpacked, so no function argument detection is needed.
Here is what I have tried:
template <typename Callable, typename Tuple>
auto geniune_apply(Callable&& callable, Tuple&& tuple)
{
return std::apply(std::forward<Callable>(callable), std::forward<Tuple>(tuple));
}
template <typename Callable, typename T, typename = typename std::enable_if<!shino::is_tuple_like<std::decay_t<T>>::value>::type>
auto geniune_apply(Callable&& callable, T&& arg)
{
return std::forward<Callable>(callable)(std::forward<T>(arg));
}
it results in ambiguity, which is what I expected. Then I tried to SFINAE on the size of the tuple instead, but I couldn't prevent compilation error on non tuple types.
Here are the test cases I'm using:
#include <cassert>
#include <iostream>
#include <stdexcept>
#include <vector>
int dummy_x(const std::tuple<int, int>&)
{
return 1;
}
int dummy_y(int y)
{
return y;
}
int main()
{
shino::geniune_apply(&dummy_x, std::tuple<int, int>(1, 1));
shino::geniune_apply(dummy_y, 1);
shino::geniune_apply(dummy_y, std::make_tuple(1));
}
Code for tuple-like, if needed. It basically tests if it is std::array or std::tuple:
template <typename T>
struct is_straight_tuple
{
static constexpr bool value = false;
constexpr operator bool()
{
return value;
}
};
template <typename ... Ts>
struct is_straight_tuple<std::tuple<Ts...>>
{
static constexpr bool value = true;
constexpr operator bool()
{
return value;
}
};
template <typename T>
struct is_std_array
{
static constexpr bool value = false;
};
template <typename T, std::size_t size>
struct is_std_array<std::array<T, size>>
{
static constexpr bool value = true;
constexpr operator bool()
{
return value;
}
};
template <typename T>
struct is_tuple_like
{
static constexpr bool value = is_std_array<T>::value || is_straight_tuple<T>::value;
constexpr operator bool()
{
return value;
}
};
The simplest way to solve this in C++14 is to just use tag-dispatch:
template <typename Callable, typename Arg>
decltype(auto) geniune_apply(Callable&& callable, Arg&& arg)
{
return details::genuine_apply(std::forward<Callable>(callable),
std::forward<Arg>(arg),
is_tuple_like<std::decay_t<Arg>>{});
}
Change your is_tuple_like to inherit from a std::integral_constant<bool, ???> instead of reimplementing the same. This would allow you to then write these two helper functions:
namespace details {
// the tuple-like case
template <typename Callable, typename Tuple>
decltype(auto) genuine_apply(Callable&&, Tuple&&, std::true_type );
// the non-tuple-like case
template <typename Callable, typename Arg>
decltype(auto) genuine_apply(Callable&&, Arg&&, std::false_type );
}
In C++17, the way nicer solution is to simply use if constexpr instead of tag dispatching. With C++ Concepts, your initial approach to the problem would actually work as is (have one unconstrained function template, and one constrained on the second argument being tuple-like).
I am trying to check if a function argument passed is unary or not, something like so
template <typename Func>
using EnableIfUnary = std::enable_if_t<std::is_same<
decltype(std::declval<Func>()(std::declval<const int&>())),
decltype(std::declval<Func>()(std::declval<const int&>()))>::value>;
template <typename Func, EnableIfUnary<Func>* = nullptr>
void do_something(Func func) { ... }
// and use like so
template <typename Type>
void foo(Type) { cout << "foo(Type)" << endl; }
template <typename Type>
void bar(Type) { typename Type::something{}; }
int main() {
do_something(foo);
return 0;
}
Is there a better way to check if a function is unary? My current approach doesn't work when the function pass in (foo() in my example) uses the type in a way that would not work with ints.
In the above case foo is legal and bar isn't, since there is no type named something in int (which is what the enable if checks for)
template<typename...>
struct is_unary_function : std::false_type {};
template<typename T, typename R>
struct is_unary_function<R(*)(T)> : std::true_type {};
Live Demo