Say I have a template declaration like this:
template <class A, class B, class C = A (&)(B)>
How would I make it so that I could have a variable amount of objects of type C? Doing class C ...c = x won't work because variadic template arguments can't have default values. So this is what I've tried:
template <typename T>
struct helper;
template <typename F, typename B>
struct helper<F(B)> {
typedef F (&type)(B);
};
template <class F, class B, typename helper<F(B)>::type ... C>
void f(C ...c) { // error
}
But up to the last part I get error messages. I don't think I'm doing this right. What am I doing wrong here?
I think you can use the following approach. First, some machinery for type traits. This allows you to determine if the types in an argument pack are homogeneous (I guess you want all functions to have the same signature):
struct null_type { };
// Declare primary template
template<typename... Ts>
struct homogeneous_type;
// Base step
template<typename T>
struct homogeneous_type<T>
{
using type = T;
static const bool isHomogeneous = true;
};
// Induction step
template<typename T, typename... Ts>
struct homogeneous_type<T, Ts...>
{
// The underlying type of the tail of the parameter pack
using type_of_remaining_parameters = typename
homogeneous_type<Ts...>::type;
// True if each parameter in the pack has the same type
static const bool isHomogeneous =
is_same<T, type_of_remaining_parameters>::value;
// If isHomogeneous is "false", the underlying type is a fictitious type
using type = typename conditional<isHomogeneous, T, null_type>::type;
};
// Meta-function to determine if a parameter pack is homogeneous
template<typename... Ts>
struct is_homogeneous_pack
{
static const bool value = homogeneous_type<Ts...>::isHomogeneous;
};
Then, some more type traits to figure out the signature of a generic function:
template<typename T>
struct signature;
template<typename A, typename B>
struct signature<A (&)(B)>
{
using ret_type = A;
using arg_type = B;
};
And finally, this is how you would define your variadic function template:
template <typename... F>
void foo(F&&... f)
{
static_assert(is_homogeneous_pack<F...>::value, "Not homogeneous!");
using fxn_type = typename homogeneous_type<F...>::type;
// This was template parameter A in your original code
using ret_type = typename signature<fxn_type>::ret_type;
// This was template parameter B in your original code
using arg_type = typename signature<fxn_type>::arg_type;
// ...
}
Here is a short test:
int fxn1(double) { }
int fxn2(double) { }
int fxn3(string) { }
int main()
{
foo(fxn1, fxn2); // OK
foo(fxn1, fxn2, fxn3); // ERROR! not homogeneous signatures
return 0;
}
Finally, if you need an inspiration on what to do once you have that argument pack, you can check out a small library I wrote (from which part of the machinery used in this answer is taken). An easy way to call all the functions in the argument pack F... f is the following (credits to #MarkGlisse):
initializer_list<int>{(f(forward<ArgType>(arg)), 0)...};
You can easily wrap that in a macro (just see Mark's answer to the link I posted).
Here is a complete, compilable program:
#include <iostream>
#include <type_traits>
using namespace std;
struct null_type { };
// Declare primary template
template<typename... Ts>
struct homogeneous_type;
// Base step
template<typename T>
struct homogeneous_type<T>
{
using type = T;
static const bool isHomogeneous = true;
};
// Induction step
template<typename T, typename... Ts>
struct homogeneous_type<T, Ts...>
{
// The underlying type of the tail of the parameter pack
using type_of_remaining_parameters = typename
homogeneous_type<Ts...>::type;
// True if each parameter in the pack has the same type
static const bool isHomogeneous =
is_same<T, type_of_remaining_parameters>::value;
// If isHomogeneous is "false", the underlying type is a fictitious type
using type = typename conditional<isHomogeneous, T, null_type>::type;
};
// Meta-function to determine if a parameter pack is homogeneous
template<typename... Ts>
struct is_homogeneous_pack
{
static const bool value = homogeneous_type<Ts...>::isHomogeneous;
};
template<typename T>
struct signature;
template<typename A, typename B>
struct signature<A (&)(B)>
{
using ret_type = A;
using arg_type = B;
};
template <typename F>
void foo(F&& f)
{
cout << f(42) << endl;
}
template <typename... F>
void foo(typename homogeneous_type<F...>::type f, F&&... fs)
{
static_assert(is_homogeneous_pack<F...>::value, "Not homogeneous!");
using fxn_type = typename homogeneous_type<F...>::type;
// This was template parameter A in your original code
using ret_type = typename signature<fxn_type>::ret_type;
// This was template parameter B in your original code
using arg_type = typename signature<fxn_type>::arg_type;
cout << f(42) << endl;
foo(fs...);
}
int fxn1(double i) { return i + 1; }
int fxn2(double i) { return i * 2; }
int fxn3(double i) { return i / 2; }
int fxn4(string s) { return 0; }
int main()
{
foo(fxn1, fxn2, fxn3); // OK
// foo(fxn1, fxn2, fxn4); // ERROR! not homogeneous signatures
return 0;
}
template <typename T>
struct helper;
template <typename F, typename B>
struct helper<F(B)> {
typedef F (*type)(B);
};
template<class F, class B>
void f()
{
}
template <class F, class B, typename... C>
void f(typename helper<F(B)>::type x, C... c)
{
std::cout << x(B(10)) << '\n';
f<F,B>(c...);
}
int identity(int i) { return i; }
int half(int i) { return i/2; }
int square(int i) { return i * i; }
int cube(int i) { return i * i * i; }
int main()
{
f<int,int>(identity,half,square,cube);
}
Here's a modified version that can deduce types:
template<class F, class B>
void f(F(*x)(B))
{
x(B());
}
template <class F, class B, typename... C>
void f(F(*x)(B), C... c)
{
f(x);
f<F,B>(c...);
}
int identity(int i) { return i; }
int half(int i) { return i/2; }
int square(int i) { return i * i; }
int cube(int i) { return i * i * i; }
int string_to_int(std::string) { return 42; }
int main()
{
f(identity,half,square,cube);
// f(identity,half,string_to_int);
}
Related
What I'm trying to do is find a clean way to implement a concept for a callable object that takes in a single parameter of type either int or long.
My first attempt was to create a single concept with a secondary template parameter to ensure the parameter type is either int or long. The problem with this approach, as seen in the example below, is that applications of this concept can't infer template parameters. For example, the usages of call() below require that template parameters be explicitly listed out.
// https://godbolt.org/z/E519s8Pso
//
#include <concepts>
#include <iostream>
// Concept for a callable that can take a single parameter or either int or long.
template<typename T, typename P>
concept MySpecialFunction =
(std::same_as<P, int> || std::same_as<P, long>)
&& requires(T t, P l) {
{ t(l) } -> std::same_as<decltype(l)>;
};
// T must be callable with 1 parameter that is either int or long!
template<typename T, typename P>
requires MySpecialFunction<T, P>
decltype(auto) call(T t) {
return t(2);
}
// Test
int square_int(int num) {
return num * num;
}
long square_long(long num) {
return num * num;
}
int main() {
std::cout << call<decltype(square_int), int>(square_int) << std::endl;
std::cout << call<decltype(square_long), long>(square_long) << std::endl;
return 0;
}
My second attempt was to explode out the concept to one for int and one for long, then combine them together in a third concept. In this version, the usages of call() below don't require that template parameters be explicitly listed out, but the concept is more verbose. Imagine how something like this would look if there were more than 20 types instead of just 2.
// https://godbolt.org/z/hchT11rMx
//
#include <concepts>
#include <iostream>
// Concept for a callable that can take a single parameter or either int or long.
template<typename T>
concept MySpecialFunction1 = requires(T t, int i) {
{ t(i) } -> std::same_as<decltype(i)>;
};
template<typename T>
concept MySpecialFunction2 = requires(T t, long l) {
{ t(l) } -> std::same_as<decltype(l)>;
};
template<typename T>
concept MySpecialFunction = MySpecialFunction1<T> || MySpecialFunction2<T>;
// T must be callable with 1 parameter that is either int or long!
template<MySpecialFunction T>
decltype(auto) call(T t) {
return t(2);
}
// Test
int square_int(int num) {
return num * num;
}
long square_long(long num) {
return num * num;
}
int main() {
std::cout << call(square_int) << std::endl;
std::cout << call(square_long) << std::endl;
return 0;
}
Is there anyway to have the conciseness / easy of understanding that the first example gives without the compiler losing the ability to infer template parameters as happens in the second example?
Since you have the types int and long baked into the concept, why don't you use something like this:
//true if Fn accepts one parameter (int or long) and have the same type as the result
template <typename Fn>
concept MySpecialFunction = requires (Fn fn) {
requires
std::same_as<int, decltype(fn(0))> ||
std::same_as<long, decltype(fn(0L))>;
};
template <MySpecialFunction Fn>
decltype(auto) call(Fn fn)
{
//think about what happens if Fn is square_long
//doesn't the type of the value (int) gets converted to long?
//that raises the question what your main purpose is
return fn(2);
}
See: https://godbolt.org/z/Y7vTrPoP5
Maybe you also want to have a look at:
https://en.cppreference.com/w/cpp/concepts/invocable
https://en.cppreference.com/w/cpp/utility/functional/invoke
After some browsing around, I came across https://stackoverflow.com/a/43526780/1196226 and https://stackoverflow.com/a/22632571/1196226. I was able to utilize these answers to build out a solution that can apply concepts to parameters concisely and without the compiler losing the ability to infer template parameters.
// https://godbolt.org/z/nh8nWxhzK
//
#include <concepts>
#include <iostream>
template <std::size_t N, typename T0, typename ... Ts>
struct typeN { using type = typename typeN<N-1U, Ts...>::type; };
template <typename T0, typename ... Ts>
struct typeN<0U, T0, Ts...> { using type = T0; };
template <std::size_t, typename F>
struct argN;
template <std::size_t N, typename R, typename ... As>
struct argN<N, R(*)(As...)> { using type = typename typeN<N, As...>::type; }; // needed for std::integral<>
template <std::size_t N, typename R, typename ... As>
struct argN<N, R(As...)> { using type = typename typeN<N, As...>::type; }; // needed for std::is_integeral_v<>
template <typename F>
struct returnType;
template <typename R, typename ... As>
struct returnType<R(*)(As...)> { using type = R; }; // works for std::integral<> / std::same_as<>
template <typename R, typename ... As>
struct returnType<R(As...)> { using type = R; }; // needed for std::is_integeral_v<>
template<typename Fn>
concept MySpecialFunction =
(std::same_as<typename argN<0U, Fn>::type, int> || std::same_as<typename argN<0U, Fn>::type, long>)
&& std::same_as<typename returnType<Fn>::type, typename argN<0U, Fn>::type>;
template<MySpecialFunction Fn>
decltype(auto) call(Fn fn) {
return fn(2);
}
// Test
int square_int(int num) {
return num * num;
}
long square_long(long num) {
return num * num;
}
static_assert( std::is_integral_v<typename argN<0U, decltype(square_int)>::type> );
static_assert( std::is_integral_v<typename returnType<decltype(square_int)>::type> );
static_assert( std::is_integral_v<typename argN<0U, decltype(square_long)>::type> );
static_assert( std::is_integral_v<typename returnType<decltype(square_long)>::type> );
int main() {
std::cout << call(square_int) << std::endl;
std::cout << call(square_long) << std::endl;
return 0;
}
I have a template function:
template<typename R, typename... T>
void function(const std::string& id, R (*f)(T...)) {
switch (sizeof...(T)) {
case 0: f0compile<R>(reinterpret_cast<void (*)()>(f)); break;
case 1: f1compile<R, T>(reinterpret_cast<void (*)()>(f)); break;
case 2: f2compile<R, T1, T2>(reinterpret_cast<void (*)()>(f)); break;
}
...
}
How can I call these functions (f0compile, f1compile, f2compile) ? How can I write the "function" ?
template<typename R>
void f0compile(void (*f)()) {
new F0<R>(f):
...
}
template<typename R, typename T>
void f1compile(void (*f)()) {
new F1<R,T>(f);
...
}
template<typename R, typename T1, typename T2>
void f2compile(void (*f)()) {
new F2<R,T1,T2>(f);
...
}
Thank you for help with these variadic template.
I add the implementation of F0 F1 F2:
template <typename R> struct F0 : F {
F0(void (*_fn)()) : F(typeid(R))
, fn(reinterpret_cast<R(*)()>(_fn))
{}
const void* f() { res = fn(); return &res; }
R res; R (*fn)();
void d() { delete this; }
};
template <typename R, typename T> struct F1 : F {
F1(void (*_fn)(), F* _opd) : F(typeid(R))
, fn(reinterpret_cast<R(*)(T)>(_fn))
, opd(autocast<T>(_opd))
{}
const void* f() { res = fn(*(T*) opd->f()); return &res; }
F* opd;
R res; R (*fn)(T);
void d() { opd->d(); delete this; }
};
template <typename R, typename T1, typename T2> struct F2 : F {
F2(void (*_fn)(), F* _opd1, F* _opd2) : F(typeid(R))
, fn(reinterpret_cast<R(*)(T1,T2)>(_fn))
, opd1(autocast<T1>(_opd1))
, opd2(autocast<T2>(_opd2))
{}
const void* f() { res = fn(*(T1*) opd1->f(), *(T2*) opd2->f()); return &res; }
F* opd1; F* opd2;
R res; R (*fn)(T1,T2);
void d() { opd1->d(); opd2->d(); delete this; }
};
Thank you
struct F {
F(const std::type_info& _type) : type(_type) {}
virtual ~F() {}
const std::type_info& type;
virtual const void* f() = 0;
virtual void d() = 0;
};
Added class F . It rapresent each function / operand on the stack
template <typename T> struct Opd : F {
Opd(T _opd) : F(typeid(T)), res(_opd) { }
const void* f() { return &res; }
T res;
void d() { delete this; }
};
Added class Opd . It represent a specific operand on the stack.
The real program is this (simplified):
double foo(double op1, double op2) {
return op1 + op2;
}
#include <functional>
#include <stack>
#include <type_traits>
class Expression {
public:
struct F {
F(const std::type_info& _type) : type(_type) {}
virtual ~F() {}
const std::type_info& type;
virtual const void* f() = 0;
virtual void d() = 0;
};
public:
Expression() : m_cexpr(NULL) {}
~Expression() {
if (m_cexpr) m_cexpr->d();
}
// function
template<typename R, typename... T> void function(R (*f)(T...), void (*compile)(void (*)(), std::stack<F*>&)) {
m_f = std::make_pair(reinterpret_cast<void (*)()>(f), compile);
}
template<typename R, typename T1, typename T2> static void f2compile(void (*f)(), std::stack<F*>& s) {
auto opd2 = s.top();
s.pop();
auto opd1 = s.top();
s.pop();
s.push(new F2<R,T1,T2>(f, opd1, opd2));
}
void compile() {
if (m_cexpr) m_cexpr->d();
std::stack<F*> s;
s.push(new Opd<double>(1));
s.push(new Opd<double>(2));
m_f.second(m_f.first, s);
m_cexpr = s.top();
s.pop();
assert(s.empty());
}
void* execute() {
return const_cast<void*>(m_cexpr->f());
}
const std::type_info& type() {
return m_cexpr->type;
}
private:
F* m_cexpr;
std::pair<void (*)(), void (*)(void (*)(), std::stack<F*>&)> m_f;
template <typename T> struct Opd : F {
Opd(T _opd) : F(typeid(T)), res(_opd) {}
const void* f() { return &res; }
T res;
void d() { delete this; }
};
template <typename R, typename T1, typename T2> struct F2 : F {
F2(void (*_fn)(), F* _opd1, F* _opd2) : F(typeid(R))
, fn(reinterpret_cast<R(*)(T1,T2)>(_fn))
, opd1(_opd1)
, opd2(_opd2)
{}
const void* f() { res = fn(*(T1*) opd1->f(), *(T2*) opd2->f()); return &res; }
F* opd1; F* opd2;
R res; R (*fn)(T1,T2);
void d() { opd1->d(); opd2->d(); delete this; }
};
};
TEST_CASE("expression") {
Expression e;
e.function(foo, e.f2compile<double, double, double>);
e.compile();
e.execute();
REQUIRE(e.type() == typeid(double));
REQUIRE(*static_cast<double*>(e.execute()) == 3);
}
And my problem is how write better code c++11 using variadic template. How write a function "fNcompile" and a function "FN" with variadic template.
I don't think you need the variadic template. Instead:
template<typename R>
void fcompile(void (*f)()) {
new F0<R>(reinterpret_cast<void (*)()>(f));
...
}
template<typename R, typename T>
void fcompile(void (*f)(T)) {
new F1<R,T>(reinterpret_cast<void (*)()>(f));
...
}
template<typename R, typename T1, typename T2>
void fcompile(void (*f)(T1, T2)) {
new F1<R,T1,T2>(reinterpret_cast<void (*)()>(f));
...
}
Now you can call fcompile<some_type>(some_func) for any some_type and any nullary/unary/binary some_func which returns void.
To answer the specific question, below are variadic FN and fNcompile as close as possible to your existing code. First, though, since you said you're working in C++11, we'll need an equivalent of std::make_index_sequence from C++14. Here's a simple one. You can search for others that are smarter about being less likely to hit compiler template limitations...
namespace cxx_compat {
template <typename T, T... Values>
struct integer_sequence {
static constexpr std::size_t size() const
{ return sizeof...(Values); }
};
template <typename T, T Smallest, T... Values>
struct make_integer_sequence_helper {
static_assert(Smallest > 0,
"make_integer_sequence argument must not be negative");
using type = typename make_integer_sequence_helper<
T, Smallest-1, Smallest-1, Values...>::type;
};
template <typename T, T... Values>
struct make_integer_sequence_helper<T, 0, Values...> {
using type = integer_sequence<T, Values...>;
};
template <typename T, T N>
using make_integer_sequence =
typename make_integer_sequence_helper<T, N>::type;
template <std::size_t... Values>
using index_sequence = integer_sequence<std::size_t, Values...>;
template <std::size_t N>
using make_index_sequence = make_integer_sequence<std::size_t, N>;
template <typename... T>
using index_sequence_for = make_index_sequence<sizeof...(T)>;
} // end namespace cxx_compat
And now, the actual FN and fNcompile:
template <typename R, typename ...T> struct FN : F {
private:
template <typename T>
using any_to_Fstar = F*;
public:
FN(void (*_fn)(), any_to_Fstar<T> ... _opd) : F(typeid(R))
, fn(reinterpret_cast<R(*)(T...)>(_fn))
, opd{_opd...}
{}
FN(R (*_fn)(T...)) : F(typeid(R)), fn(_fn), opd() {}
const void* f() {
f_helper(cxx_compat::index_sequence_for<T...>{});
return &res;
}
std::array<F*, sizeof...(T)> opd;
R res; R (*fn)(T...);
void d() {
for (F* o : opd)
o->d();
delete this;
}
private:
template <std::size_t... Inds>
void f_helper(cxx_compat::index_sequence<Inds...>)
{ res = fn(*(T*) opd[Inds]->f() ...); }
};
template<typename R, typename... T>
static void fNcompile(void (*f)(), std::stack<F*>& s) {
auto* f_obj = new FN<R, T...>(f);
for (std::size_t ind = sizeof...(T); ind > 0;) {
f_obj->opd[--ind] = s.top();
s.pop();
}
s.push(f_obj);
}
What's going on:
To actually call the function pointer, we need access to a number of function arguments at the same time, so to replace the named members opd1, opd2 with a number of F* pointers determined by template instantiation, we use a std::array<F*, sizeof...(T)>, since sizeof...(T) is the number of argument types provided to the template.
For compatibility with the F2 constructor you declared, any_to_Fstar<T> ... _opd declares a number of constructor parameters to match the number of T template arguments, all with the same type F*. (But now fNcompile uses the additional constructor taking just the function pointer instead, and sets the array members afterward.)
To get at these pointers and pass them all to fn in one expression, we need to expand some sort of variadic pack. Here's where index_sequence comes in:
index_sequence_for<T...> is a type alias for index_sequence with a sequence of numbers counting up from zero as template arguments. For example, if sizeof...(T) is 4, then index_sequence_for<T...> is index_sequence<0, 1, 2, 3>.
f just calls a private function f_helper, passing it an object of that index_sequence_for<T...> type.
The compiler can deduce the template argument list for f_helper from matching the index_sequence types: Inds... must be that same sequence of numbers counting up from zero.
In the f_helper body, the expression fn(*(T*) opd[Inds]->f() ...) is instantiated by expanding both the template parameter packs T and Inds to get one list of function arguments for calling fn.
However, use of void pointers and reinterpret_cast is dangerous and rarely actually necessary in C++. There's almost always a safer way using templates. So I'd redesign this to be something more like:
#include <type_traits>
#include <typeinfo>
#include <stdexcept>
#include <memory>
#include <stack>
namespace cxx_compat {
// Define integer_sequence and related templates as above.
template <typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&& ... args)
{
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
} // end namespace cxx_compat
class bad_expression_type : public std::logic_error
{
public:
bad_expression_type(const std::type_info& required,
const std::type_info& passed)
: logic_error("bad_argument_type"),
required_type(required),
passed_type(passed) {}
const std::type_info& required_type;
const std::type_info& passed_type;
};
class Expression
{
public:
class F
{
public:
F() noexcept = default;
F(const F&) = delete;
F& operator=(const F&) = delete;
virtual ~F() = default;
virtual const std::type_info& type() const noexcept = 0;
virtual void compile(std::stack<std::unique_ptr<F>>&) = 0;
template <typename R>
R call_R() const;
};
using F_ptr = std::unique_ptr<F>;
using F_stack = std::stack<F_ptr>;
template <typename R>
class Typed_F : public F
{
public:
const std::type_info& type() const noexcept override
{ return typeid(R); }
virtual R call() const = 0;
};
// Accepts any callable: function pointer, lambda, std::function,
// other class with operator().
template <typename R, typename... T, typename Func,
typename = typename std::enable_if<std::is_convertible<
decltype(std::declval<const Func&>()(std::declval<T>()...)),
R>::value>::type>
void function(Func func)
{
store_func<R, T...>(std::move(func));
}
// Overload for function pointer that does not need explicit
// template arguments:
template <typename R, typename... T>
void function(R (*fptr)(T...))
{
store_func<R, T...>(fptr);
}
template <typename T>
void constant(const T& value)
{
store_func<T>([value](){ return value; });
}
void compile(F_stack& stack)
{
m_cexpr->compile(stack);
}
private:
template <typename Func, typename R, typename... T>
class F_Impl : public Typed_F<R>
{
public:
F_Impl(Func func) : m_func(std::move(func)) {}
void compile(F_stack& stack) override {
take_args_helper(stack, cxx_compat::index_sequence_for<T...>{});
}
R call() const override {
return call_helper(cxx_compat::index_sequence_for<T...>{});
}
private:
template <typename Arg>
int take_one_arg(std::unique_ptr<Typed_F<Arg>>& arg, F_stack& stack)
{
auto* fptr = dynamic_cast<Typed_F<Arg>*>(stack.top().get());
if (!fptr)
throw bad_expression_type(
typeid(Arg), stack.top()->type());
arg.reset(fptr);
stack.top().release();
stack.pop();
return 0;
}
template <std::size_t... Inds>
void take_args_helper(F_stack& stack, cxx_compat::index_sequence<Inds...>)
{
using int_array = int[];
(void) int_array{ take_one_arg(std::get<Inds>(m_args), stack) ..., 0 };
}
template <std::size_t... Inds>
R call_helper(cxx_compat::index_sequence<Inds...>) const {
return m_func(std::get<Inds>(m_args)->call()...);
}
Func m_func;
std::tuple<std::unique_ptr<Typed_F<T>>...> m_args;
};
template <typename R, typename... T, typename Func>
void store_func(Func func)
{
m_cexpr = cxx_compat::make_unique<F_Impl<Func, R, T...>>(
std::move(func));
}
F_ptr m_cexpr;
};
template <typename R>
R Expression::F::call_R() const
{
auto* typed_this = dynamic_cast<const Typed_F<R>*>(this);
if (!typed_this)
throw bad_expression_type(typeid(R), type());
return typed_this->call();
}
TEST_CASE("expression") {
Expression a;
a.constant(1.0);
Expression b;
b.constant(2.0);
Expression c;
c.function(+[](double x, double y) { return x+y; });
Expression::F_stack stack;
a.compile(stack);
REQUIRE(stack.size() == 1);
b.compile(stack);
REQUIRE(stack.size() == 2);
c.compile(stack);
REQUIRE(stack.size() == 1);
REQUIRE(stack.top() != nullptr);
REQUIRE(stack.top()->type() == typeid(double));
REQUIRE(stack.top()->call_R<double>() == 3.0);
}
It would also be possible, but a bit tricky, to support reference and const variations of the argument and result types, for example, using a std::string(*)() function as an argument to an unsigned int(*)(const std::string&) function.
Problem description:
C++17 introduces std::invocable<F, Args...>, which is nice to detect if a type... is invocable with the given arguments. However, would there be a way to do it for any arguments for functors (because combinations of the existing traits of the standard library already allow to detect functions, function pointers, function references, member functions...)?
In other words, how to implement the following type trait?
template <class F>
struct is_functor {
static constexpr bool value = /*using F::operator() in derived class works*/;
};
Example of use:
#include <iostream>
#include <type_traits>
struct class0 {
void f();
void g();
};
struct class1 {
void f();
void g();
void operator()(int);
};
struct class2 {
void operator()(int);
void operator()(double);
void operator()(double, double) const noexcept;
};
struct class3 {
template <class... Args> constexpr int operator()(Args&&...);
template <class... Args> constexpr int operator()(Args&&...) const;
};
union union0 {
unsigned int x;
unsigned long long int y;
template <class... Args> constexpr int operator()(Args&&...);
template <class... Args> constexpr int operator()(Args&&...) const;
};
struct final_class final {
template <class... Args> constexpr int operator()(Args&&...);
template <class... Args> constexpr int operator()(Args&&...) const;
};
int main(int argc, char* argv[]) {
std::cout << is_functor<int>::value;
std::cout << is_functor<class0>::value;
std::cout << is_functor<class1>::value;
std::cout << is_functor<class2>::value;
std::cout << is_functor<class3>::value;
std::cout << is_functor<union0>::value;
std::cout << is_functor<final_class>::value << std::endl;
return 0;
}
should output 001111X. In an ideal world, X should be 1, but I don't think it's doable in C++17 (see bonus section).
Edit:
This post seems to present a strategy that solves the problem. However, would there be a better/more elegant way to do it in C++17?
Bonus:
And as a bonus, would there be a way to make it work on final types (but that's completely optional and probably not doable)?
Building on my answer to my answer to this qustion, i was able to solve your problem, including the bonus one :-)
The following is the code posted in the other thread plus some little tweaks to get a special value when an object can't be called. The code needs c++17, so currently no MSVC...
#include<utility>
constexpr size_t max_arity = 10;
struct variadic_t
{
};
struct not_callable_t
{
};
namespace detail
{
// it is templated, to be able to create a
// "sequence" of arbitrary_t's of given size and
// hece, to 'simulate' an arbitrary function signature.
template <size_t>
struct arbitrary_t
{
// this type casts implicitly to anything,
// thus, it can represent an arbitrary type.
template <typename T>
operator T&& ();
template <typename T>
operator T& ();
};
template <typename F, size_t... Is,
typename U = decltype(std::declval<F>()(arbitrary_t<Is>{}...))>
constexpr auto test_signature(std::index_sequence<Is...>)
{
return std::integral_constant<size_t, sizeof...(Is)>{};
}
template <size_t I, typename F>
constexpr auto arity_impl(int) -> decltype(test_signature<F>(std::make_index_sequence<I>{}))
{
return {};
}
template <size_t I, typename F, std::enable_if_t<(I == 0), int> = 0>
constexpr auto arity_impl(...) {
return not_callable_t{};
}
template <size_t I, typename F, std::enable_if_t<(I > 0), int> = 0>
constexpr auto arity_impl(...)
{
// try the int overload which will only work,
// if F takes I-1 arguments. Otherwise this
// overload will be selected and we'll try it
// with one element less.
return arity_impl<I - 1, F>(0);
}
template <typename F, size_t MaxArity = 10>
constexpr auto arity_impl()
{
// start checking function signatures with max_arity + 1 elements
constexpr auto tmp = arity_impl<MaxArity + 1, F>(0);
if constexpr(std::is_same_v<std::decay_t<decltype(tmp)>, not_callable_t>) {
return not_callable_t{};
}
else if constexpr (tmp == MaxArity + 1)
{
// if that works, F is considered variadic
return variadic_t{};
}
else
{
// if not, tmp will be the correct arity of F
return tmp;
}
}
}
template <typename F, size_t MaxArity = max_arity>
constexpr auto arity(F&& f) { return detail::arity_impl<std::decay_t<F>, MaxArity>(); }
template <typename F, size_t MaxArity = max_arity>
constexpr auto arity_v = detail::arity_impl<std::decay_t<F>, MaxArity>();
template <typename F, size_t MaxArity = max_arity>
constexpr bool is_variadic_v = std::is_same_v<std::decay_t<decltype(arity_v<F, MaxArity>)>, variadic_t>;
// HERE'S THE IS_FUNCTOR
template<typename T>
constexpr bool is_functor_v = !std::is_same_v<std::decay_t<decltype(arity_v<T>)>, not_callable_t>;
Given the classes in yout question, the following compiles sucessfully (you can even use variadic lambdas:
constexpr auto lambda_func = [](auto...){};
void test_is_functor() {
static_assert(!is_functor_v<int>);
static_assert(!is_functor_v<class0>);
static_assert(is_functor_v<class1>);
static_assert(is_functor_v<class2>);
static_assert(is_functor_v<class3>);
static_assert(is_functor_v<union0>);
static_assert(is_functor_v<final_class>);
static_assert(is_functor_v<decltype(lambda_func)>);
}
See also a running example here.
I have this code:
template<class T1, class T2>
class Pair
{
private:
T1 first;
T2 second;
public:
void SetFirst(T1 first)
{
this.first = first;
}
void SetSecond(T2 second)
{
this.second = second;
}
T1 GetFirst()
{
return first;
}
T2 GetSecond()
{
return second;
}
};
How could I implement two single methods SetValue() and GetValue(), instead of the four I have, that decides depending on parameters which generic type that should be used? For instance I'm thinking the GetValue() method could take an int parameter of either 1 or 2 and depending on the number, return either a variable of type T1 or T2. But I don't know the return type beforehand so is there anyway to solve this?
Not sure to understand what do you want and not exactly what you asked but...
I propose the use of a wrapper base class defined as follows
template <typename T>
class wrap
{
private:
T elem;
public:
void set (T const & t)
{ elem = t; }
T get () const
{ return elem; }
};
Now your class can be defined as
template <typename T1, typename T2>
struct Pair : wrap<T1>, wrap<T2>
{
template <typename T>
void set (T const & t)
{ wrap<T>::set(t); }
template <typename T>
T get () const
{ return wrap<T>::get(); }
};
or, if you can use C++11 and variadic templates and if you define a type traits getType to get the Nth type of a list,
template <std::size_t I, typename, typename ... Ts>
struct getType
{ using type = typename getType<I-1U, Ts...>::type; };
template <typename T, typename ... Ts>
struct getType<0U, T, Ts...>
{ using type = T; };
you can define Pair in a more flexible way as follows
template <typename ... Ts>
struct Pair : wrap<Ts>...
{
template <typename T>
void set (T const & t)
{ wrap<T>::set(t); }
template <std::size_t N, typename T>
void set (T const & t)
{ wrap<typename getType<N, Ts...>::type>::set(t); }
template <typename T>
T get () const
{ return wrap<T>::get(); }
template <std::size_t N>
typename getType<N, Ts...>::type get ()
{ return wrap<typename getType<N, Ts...>::type>::get(); }
};
Now the argument of set() can select the correct base class and the correct base element
Pair<int, long> p;
p.set(0); // set the int elem
p.set(1L); // set the long elem
otherwise, via index, you can write
p.set<0U>(3); // set the 1st (int) elem
p.set<1U>(4); // set the 2nd (long) elem
Unfortunately, the get() doesn't receive an argument, so the type have to be explicited (via type or via index)
p.get<int>(); // get the int elem value
p.get<long>(); // get the long elem value
p.get<0U>(); // get the 1st (int) elem value
p.get<1U>(); // get the 2nd (long) elem value
Obviously, this didn't work when T1 is equal to T2
The following is a (C++11) full working example
#include <iostream>
template <std::size_t I, typename, typename ... Ts>
struct getType
{ using type = typename getType<I-1U, Ts...>::type; };
template <typename T, typename ... Ts>
struct getType<0U, T, Ts...>
{ using type = T; };
template <typename T>
class wrap
{
private:
T elem;
public:
void set (T const & t)
{ elem = t; }
T get () const
{ return elem; }
};
template <typename ... Ts>
struct Pair : wrap<Ts>...
{
template <typename T>
void set (T const & t)
{ wrap<T>::set(t); }
template <std::size_t N, typename T>
void set (T const & t)
{ wrap<typename getType<N, Ts...>::type>::set(t); }
template <typename T>
T get () const
{ return wrap<T>::get(); }
template <std::size_t N>
typename getType<N, Ts...>::type get ()
{ return wrap<typename getType<N, Ts...>::type>::get(); }
};
int main()
{
//Pair<int, int> p; compilation error
Pair<int, long, long long> p;
p.set(0);
p.set(1L);
p.set(2LL);
std::cout << p.get<int>() << std::endl; // print 0
std::cout << p.get<long>() << std::endl; // print 1
std::cout << p.get<long long>() << std::endl; // print 2
p.set<0U>(3);
p.set<1U>(4);
p.set<2U>(5);
std::cout << p.get<0U>() << std::endl; // print 3
std::cout << p.get<1U>() << std::endl; // print 4
std::cout << p.get<2U>() << std::endl; // print 5
}
C++ is statically typed, so the argument given must be a template-argument instead a function-argument.
And while it will look like just one function each to the user, it's really two.
template <int i = 1> auto GetValue() -> std::enable_if_t<i == 1, T1> { return first; }
template <int i = 2> auto GetValue() -> std::enable_if_t<i == 2, T2> { return second; }
template <int i = 1> auto SetValue(T1 x) -> std::enable_if_t<i == 1> { first = x; }
template <int i = 2> auto SetValue(T2 x) -> std::enable_if_t<i == 2> { second = x; }
I use SFINAE on the return-type to remove the function from consideration unless the template-argument is right.
For this particular situation, you should definitely prefer std::pair or std::tuple.
You can simply overload SetValue() (provided T1 and T2 can be distinguished, if not you have a compile error):
void SetValue(T1 x)
{ first=x; }
void SetValue(T2 x)
{ second=x; }
Then, the compiler with find the best match for any call, i.e.
Pair<int,double> p;
p.SetValue(0); // sets p.first
p.SetValue(0.0); // sets p.second
With GetValue(), the information of which element you want to retrieve cannot be inferred from something like p.GetValue(), so you must provide it somehow. There are several options, such as
template<typename T>
std::enable_if_t<std::is_same<T,T1>,T>
GetValue() const
{ return first; }
template<typename T>
std::enable_if_t<std::is_same<T,T2>,T>
GetValue() const
{ return second; }
to be used like
auto a = p.GetValue<int>();
auto b = p.GetValue<double>();
but your initial version is good enough.
This question is related to one on SO a couple of years ago by Georg Fritzsche about transforming a parameter pack (Is it possible to transform the types in a parameter pack?). In the end, the individual types in the parameter pack can be transformed, e.g. by converting to corresponding pointer types.
I am wondering if it is possible to use this technique to write one standard function/functor and a set of wrapper functions (out of one template), so that the wrappers can take parameters of equivalent types and then call the standard function to do actual work.
Using the answer by Johannes Schaub - litb the original example below. Is it possible to write one template f, which can take any combinations of int/int*,char/char* and call a common function f_std(int*,char*) to do the work. (The number of parameters is not pre-specified.)
--- Update ---
For example, given int i; char c;, is it possible to write a caller using pack transformation such that the following works
call_ptr(f_std,i,c);
call_ptr(f_std,&i,c);
call_ptr(f_std,i,&c);
What I've tried so far is listed below (updated to clarify.). Basically, I tried to accept an list of not necessarily pointer types and convert them to pointer types, before making call to a std::function that takes pointer types. But the code does not compile. I don't know how to write a helper function to accept one function with a standard signature, but accept a parameter pack of something else.
Thanks in advance
#include <type_traits>
#include <functional>
using namespace std;
template<class... Args> struct X {};
template<class T> struct make_pointer { typedef T* type; };
template<class T> struct make_pointer<T*> { typedef T* type; };
template<template<typename...> class List,
template<typename> class Mod,
typename ...Args>
struct magic {
typedef List<typename Mod<Args>::type...> type;
};
/////////////////
// trying to convert parameter pack to pointers
template<class T> T* make_ptr(T x) { return &x; }
template<class T> T* make_ptr(T* x) { return x; }
template <typename Value, typename ...Args>
class ByPtrFunc
{
public:
typedef typename magic<X, make_pointer, Args...>::type PArgs;
Value operator()(Args... args) { return f(make_ptr(args)...); }
private:
std::function<Value (PArgs...)> _ptr_func;
}; //ByPtrFunc
//helper function to make call
template<typename A, typename ...Args>
static A call_ptr(std::function<A (Args...)> f, Args... args) {
return ByPtrFunc<A, Args...>{f}(args ...);
}
int main() {
typedef magic<X, make_pointer, int*, char>::type A;
typedef X<int*, char*> B;
static_assert(is_same<A, B>::value, ":(");
int i=0; char c='c';
function<int (int* pa,char* pb)> f_std = [](int* pa,char* pb)->int {return *pa + * pb;};
f_std(&i,&c);
//////////////////
//Is the following possible.
call_ptr(f_std,i,c);
call_ptr(f_std,&i,c);
call_ptr(f_std,i,&c);
return 0;
}
This answers your question syntax-wise, if I've understood it correctly: yes, it's possible.
// given int or char lvalue, returns its address
template<class T>
T* transform(T& t) {
return &t;
}
// given int* or char*, simply returns the value itself
template<class T>
T* transform(T* t) {
return t;
}
// prints out the address corresponding to each of its arguments
void f_std() {
}
template<class Arg, class... Args>
void f_std(Arg arg, Args... args) {
std::cout << (void*)arg << std::endl;
f_std(args...);
}
// converts int to int*, char to char*, then calls f_std
template<class... Args>
void f(Args... args) {
f_std(transform(args)...);
}
Unfortunately, calling f will pass int and char arguments by value, and hence copy them. To fix this, use perfect forwarding in the definition of f:
template<class... Args>
void f(Args&&... args) {
f_std(transform(std::forward<Args>(args))...);
}
Driver:
int main() {
int x = 1;
char c = 'a';
cout << (void*)&x << endl;
cout << (void*)&c << endl;
f(x, &x, c, &c);
}
Output (example; ran it on my machine just now):
0x7fff36fb5ebc
0x7fff36fb5ebb
0x7fff36fb5ebc
0x7fff36fb5ebc
0x7fff36fb5ebb
0x7fff36fb5ebb
Following may help:
template <typename T> T* make_pointer(T& t) { return &t; }
template <typename T> T* make_pointer(T* t) { return t; }
template <typename Ret, typename... Args, typename ...Ts>
Ret call_ptr(std::function<Ret (Args*...)> f, Ts&&...args)
{
static_assert(sizeof...(Args) == sizeof...(Ts), "Bad parameters");
f(make_pointer(std::forward<Ts>(args))...);
}
Now, use it:
void f_std(int*, char*) { /* Your code */ }
int main(int argc, char *argv[])
{
int i;
char c;
std::function<void (int*, char*)> f1 = f_std;
call_ptr(f1, i, c);
call_ptr(f1, i, &c);
call_ptr(f1, &i, c);
call_ptr(f1, &i, &c);
return 0;
}
For reference, below is what worked for me, based on the accepted answer #Jarod42 and the type transformation "magic". slightly more general and with added type checking. Turns out type-transformation is simply a pattern expansion.
#include <type_traits>
#include <functional>
#include <iostream>
using namespace std;
/////////////////
// convert parameter pack to pointers
//types
template<class T> struct make_ptr_t { typedef T* type; };
template<class T> struct make_ptr_t<T*> { typedef T* type; };
//values
template<class T> T* make_ptr(T& x) { return &x; }
template<class T> T* make_ptr(T* x) { return x; }
/////////////////////////////////////
// (optional) only for type checking
template<class... Args> struct X {};
template<template<typename...> class List,
template<typename> class Mod,
typename ...Args>
struct magic {
typedef List<typename Mod<Args>::type...> type;
};
//helper function to make call
template<typename A, typename ...PArgs, typename ...Args>
static A call_ptr(std::function<A (PArgs...)> f, Args... args) {
static_assert(is_same<X<PArgs...>,typename magic<X, make_ptr_t, Args...>::type>::value, "Bad parameters for f in call_ptr()"); //type checking
return f(make_ptr(args)...);
}
int main() {
int i=0; char c='c'; string s="c";
function<int (int* pa,char* pb)> f_std = [](int* pa,char* pb)->int {return *pa + * pb;};
f_std(&i,&c);
cout << call_ptr(f_std,i,c) << endl;
cout << call_ptr(f_std,&i,c) << endl;
cout << call_ptr(f_std,i,&c) << endl;
//cout << call_ptr(f_std,i,s) << endl; //complains about bad parameters.
return 0;
}