C++ void argument with variadic template - c++

I have the following template in my C++ class:
template <typename ReturnType, typename... Args>
ReturnType run(void* context, const Args&... args) {
/* Do the actual job. */
}
For practical reason, I need to specify it for ReturnType void. I need to keep the list of args because they are still useful.
I tried the following but neither works:
template <typename... Args>
void run(void* context, const Args&... args) {
/* Do the actual job. */
}
and
template <typename ReturnType = void, typename... Args>
void run(void* context, const Args&... args) {
/* Do the actual job. */
}
So.. what is the correct way to specify with the void argument in my example?

In C++17, this is simple: use if constexpr:
template <typename ReturnType, typename... Args>
ReturnType run(void* context, const Args&... args) {
if constexpr (std::is_void_v<ReturnType>) {
/* void case */
} else {
/* non-void case */
}
}
Before C++17, the best way to do this is with tag dispatching. Create a new class template that just encodes the type, so that you can pass it as an argument:
template <typename T> struct tag { };
template <typename ReturnType, typename... Args>
ReturnType run_impl(tag<ReturnType>, void* context, const Args&... args) {
/* non-void case */
}
template <typename... Args>
void run_impl(tag<void>, void* context, const Args&... args) {
/* void case */
}
template <typename ReturnType, typename... Args>
ReturnType run(void* context, const Args&... args) {
return run_impl(tag<ReturnType>{}, context, args...);
}

Related

Determine the return type of a callable passed to a template

I have a simple wrapper template that allows free functions (e.g. open() close() etc) to passed as template parameters. The code is as follows:
template <auto fn, typename ReturnType=void>
struct func_wrapper {
template<typename... Args>
constexpr ReturnType operator()(Args&&... args) const {
if constexpr( std::is_same<ReturnType, void>::value) {
fn(std::forward<Args>(args)...);
} else {
return fn(std::forward<Args>(args)...);
}
}
};
This is used as follows:
void CloseFunc2(int a);
into OpenFunc2(const std::string&, int flags);
using FileWrapper2 = DevFileWrapper<func_wrapper<OpenFunc2, int>,
func_wrapper<CloseFunc2>>;
The code is working fine but I would like to remove the requirement to manually specify ReturnType when creating a func_wrapper.
I tried using std::result_of but that failed because fn is a non type template parameter, e.g.:
template<typename... Args>
constexpr auto operator()(Args&&... args) const
-> std::invoke_result<fn(std::forward<Args>(args)...)>::type {
if constexpr( std::is_same<ReturnType, void>::value) {
fn(std::forward<Args>(args)...);
} else {
return fn(std::forward<Args>(args)...);
}
}
the error is:
template-parameter-callable.cpp:48:71: error: template argument for
template type parameter must be a type
constexpr auto operator()(Args&&... args) const ->
std::invoke_result<fn(std::forward<Args>(args)...)>::type {
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/
usr/include/c++/v1/type_traits:4009:17:
note: template parameter is declared here
template <class _Fn, class... _Args>
^
1 error generated.
How can I deduce the return type of calling fn at compile time?
template <auto fn>
struct func_wrapper {
template<typename... Args>
constexpr decltype(auto) operator()(Args&&... args) const {
return fn(std::forward<Args>(args)...);
}
};
have you tried this?
I think that works in c++17. Definitely in c++20.
The return type of a callable cannot be determined unless you also know the arguments you are calling it with.
I can extract the return type, but I don't think you need it.
template <auto fn>
struct func_wrapper {
template<typename... Args>
constexpr decltype(auto) operator()(Args&&... args) const {
using ReturnType = std::invoke_result_t<
decltype(fn),
Args&&...
>;
return fn(std::forward<Args>(args)...);
}
};
but as you can see we don't need it.
return f(x) when f returns void is (now) legal.

How to resolve constructor signature in factory function

I want to support one of two possible signatures of the constructor of the class T when creating its instance in the create(...) function below:
template <class Т, typename... Args>
T* create(Special* s, Args&&... args) {
T* t =
// If such a constructor exists, this:
new T(s, std::forward<Args>(args)...);
// Otherwise, this:
new T(std::forward<Args>(args)...);
}
I tried a few monstrous template constructions that did not cut it. The solution for resolving a member function involves SFINAE-failing a decltype of a member function, but this is not apparently possible with a constructor, as it does not have a signature type of its own.
Is this even possible in C++11, and is there any library support?
Just use std::is_constructible:
namespace detail
{
template<typename T, typename... Ts>
auto create(std::true_type, Special* s, Ts&&... args) {
return new T(s, std::forward<Ts>(args)...);
}
template<typename T, typename... Ts>
auto create(std::false_type, Special*, Ts&&... args) {
return new T(std::forward<Ts>(args)...);
}
}
template<class T, typename... Args>
T* create(Special* s, Args&&... args) {
using tag = is_constructible<T, Special*, Args...>;
return detail::create<T>(tag{}, s, std::forward<Args>(args)...);
}
live demo
template <class Т, typename... Args>
T* create_impl(std::true_type, Special* s, Args&&... args) {
return new T(s, std::forward<Args>(args)...);
}
template <class Т, typename... Args>
T* create_impl(std::false_type, Special*, Args&&... args) {
return new T(std::forward<Args>(args)...);
}
template <class Т, typename... Args>
T* create(Special* s, Args&&... args) {
T* t = create_impl<T>(std::is_constructible<T, Special*&, Args&&...>{},
s, std::forward<Args>(args)...);
// ...
return t;
}

How does std::invoke(C++1z) work?

namespace detail {
template <class F, class... Args>
inline auto INVOKE(F&& f, Args&&... args) ->
decltype(std::forward<F>(f)(std::forward<Args>(args)...)) {
return std::forward<F>(f)(std::forward<Args>(args)...);
}
template <class Base, class T, class Derived>
inline auto INVOKE(T Base::*pmd, Derived&& ref) ->
decltype(std::forward<Derived>(ref).*pmd) {
return std::forward<Derived>(ref).*pmd;
}
template <class PMD, class Pointer>
inline auto INVOKE(PMD pmd, Pointer&& ptr) ->
decltype((*std::forward<Pointer>(ptr)).*pmd) {
return (*std::forward<Pointer>(ptr)).*pmd;
}
template <class Base, class T, class Derived, class... Args>
inline auto INVOKE(T Base::*pmf, Derived&& ref, Args&&... args) ->
decltype((std::forward<Derived>(ref).*pmf)(std::forward<Args>(args)...)) {
return (std::forward<Derived>(ref).*pmf)(std::forward<Args>(args)...);
}
template <class PMF, class Pointer, class... Args>
inline auto INVOKE(PMF pmf, Pointer&& ptr, Args&&... args) ->
decltype(((*std::forward<Pointer>(ptr)).*pmf)(std::forward<Args>(args)...)) {
return ((*std::forward<Pointer>(ptr)).*pmf)(std::forward<Args>(args)...);
}
} // namespace detail
template< class F, class... ArgTypes>
decltype(auto) invoke(F&& f, ArgTypes&&... args) {
return detail::INVOKE(std::forward<F>(f), std::forward<ArgTypes>(args)...);
}
I saw the implementation above from here:
http://en.cppreference.com/w/cpp/utility/functional/invoke
Then I wonder how the compilers match the exact version required. Does SFINAE work on trailing return type?
does SFINAE work on tailing return type?
Yes. The trailing return type syntax doesn't enable any new functionality, it merely makes it easier to write some cases of return types that depend on parameter types.
template <class F, class... Args>
inline auto INVOKE(F&& f, Args&&... args) ->
decltype(std::forward<F>(f)(std::forward<Args>(args)...) { ... }
could have equivalently been written as
template <class F, class... Args>
inline decltype(std::forward<F>(std::declval<F&>())(std::forward<Args>(std::declval<Args&>())...))
INVOKE(F&& f, Args&&... args) { ... }
for instance, and the same for all the others. That could be simplified, but even if you simplify it, the fact that the return type cannot use the same syntax as the return expression makes it hard to follow what's going on. Hence the new syntax.
The only time SFINAE doesn't work is for deduced return types. They also use auto, but it's not the auto keyword by itself that disables SFINAE.

Unpack ts... to t0.a(), t0.b(), t1.a(), t1.b(),

I want to call one variadic function from another variadic function in the following manner:
template <typename ...Ts>
void f(Ts const & ...) { /* ... */ }
template <typename ...Args>
void g(Args const & ...args)
{
// shall call f(arg0.a(), arg0.b(), arg1.a(), arg1.b(), ...)
}
I have done it the following way:
struct sentinel_t { };
template <typename Arg, typename ...Args>
void g_impl(Arg const & arg, Args const & ...args)
{
g_impl(args..., arg.a(), arg.b());
}
template <typename ...Ts>
void g_impl(sentinel_t , Ts const & ...ts)
{
f(ts...);
}
template <typename ...Args>
void g(Args const & ...args)
{
g_impl(args..., sentinel_t{});
}
Is there another/better way to implement this pattern?
template <class... Args> void g(Args const &... args) {
impl::apply([](auto const &... p) { f(p...); },
std::tuple_cat(std::forward_as_tuple(args.a(), args.b())...));
}
until std::apply will be standardized you can make your own pretty simple with c++14 (taken from the referenced paper):
namespace impl {
template <typename F, typename Tuple, size_t... I>
decltype(auto) apply_impl(F &&f, Tuple &&t, std::index_sequence<I...>) {
return std::forward<F>(f)(std::get<I>(std::forward<Tuple>(t))...);
}
template <typename F, typename Tuple> decltype(auto) apply(F &&f, Tuple &&t) {
using Indices =
std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>;
return apply_impl(std::forward<F>(f), std::forward<Tuple>(t), Indices{});
}
}
You may do
namespace detail
{
// dispatcher
template <typename T>
decltype(auto) call_a_b(std::integral_constant<std::size_t, 0u>, const T& arg) {return arg.a();}
// dispatcher
template <typename T>
decltype(auto) call_a_b(std::integral_constant<std::size_t, 1u>, const T& arg) {return arg.b();}
template <std::size_t... Is, typename Tuple>
void call_f_a_b(std::index_sequence<Is...>, const Tuple& tuple)
{
f(call_a_b(std::integral_constant<std::size_t, Is % 2>{}, std::get<Is / 2>(tuple))...);
}
}
template <typename...Ts>
void g(const Ts&... args)
{
return detail::call_f_a_b(std::make_index_sequence<2 * sizeof...(Ts)>{}, std::tie(args...));
// call f(arg0.a(), arg0.b(), arg1.a(), arg1.b(), ...)
}
Live Demo

C++11 Class template method specialization and variadic templates

I am trying to construct the following kind of templated method using GCC and C++11:
class Foo
{
private:
//Device handle
cudnnHandle_t handle_;
//Batch sizes
std::vector<int> batch_;
public:
Foo(cudnnHandle_t handle, std::vector<int> batch) : handle_(handle), batch_(batch) {}
template<typename T, typename... Args> T create(Args&&... args)
{
if(std::is_base_of<Element,T>::value)
return T(handle_,args...);
if(std::is_same<Block,T>::value)
return T(handle_,batch_,args...);
}
};
This however fails to compile due to the return statements. In case where T is Block, return T(handle_,args...) will fail and in case T is a Element base type return T(handle,batch_,args...) will fail.
I tried partial template specialization which is not allowed:
template<typename T, typename... Args> T create(Args&&... args)
{
return T(handle_,args...);
}
template<typename... Args> Block create<Block>(Args&&... args)
{
return Block(handle_,batch_,args...);
}
And i tried full template specialization which is apparently not supported for variadic templates by GCC:
template<typename T, typename... Args> T create(Args&&... args)
{
return T(handle_,args...);
}
template<> Block create<Block,int>(int n)
{
return Block(handle_,batch_,n);
}
So how would i go about creating a function like create?
Kind regards.
Try taking advantage of SFINAE like this
template <typename T, typename... Args,
typename std::enable_if<std::is_base_of<Element, T>::value>::type* = nullptr>
T create(Args&&... args) {
return T(handle_, std::forward<Args>(args)...);
}
template <typename T, typename... Args,
typename std::enable_if<std::is_same<Block, T>::value>::type* = nullptr>
T create(Args&&... args) {
return T(handle_, batch_, std::forward<Args>(args)...);
}