We implement a system that passes callbacks to object-instance member-functions. This works nicely, see the code below. The problem is that the current state of the implementation handles only non-const member functions.
The code below compiles and demonstrates that the system is working. As soon as the /* const */ is included, it no longer compiles.
The error messages are localized not English, but the first message is 'incomplete type'.
Logically, a call to a const member-function should be not more constrained than a call to a non-const member-function, so it seems that the basic goal is sensible.
It is clear that the type of a const-member differs from that of a non-const member. The problem is that we do not find a way to express to the compiler that the code is also valid for const members.
Where and how in the shown WrapP can we express that a const is acceptable? Is it possible to define a single template that accepts both, const and non-const, member functions?
#include <algorithm>
#include <functional>
#include <iostream>
using std::cout;
using std::endl;
template <auto F>
struct WrapP;
template <typename T, typename R, typename ... Args, R(T::* F)(Args...)>
struct WrapP<F> {
T* obj_;
WrapP(T* instance) : obj_(instance) {}
auto operator()(Args... args) const {
return (obj_->*F)(args...);
}
};
struct foo {
// Const below is needed, but could not be activated.
auto bar(double) /* const */ -> int {
return 314; };
};
int main() {
foo x;
// Create a functor for foo::bar
WrapP<&foo::bar> fp{ &x };
// Call the functor.
std::cout << fp( 3.14159265 ) << std::endl;
return 0;
}
If you want to specialize WrapP for a const member function, you need to specify that:
template <typename T, typename R, typename ... Args, R(T::* F)(Args...) const>
struct WrapP<F> { // ^___^
// ...
};
As far as I'm aware, there isn't a way to allow for either const or non-const member function pointers in a template parameter list, so you'll have to write separate specializations for those cases.
Do not specialize WrapP -- instead keep taking auto F as your template parameter, and then extract the information you need using something like Boost.CallableTraits or your own homegrown solution:
template <auto F>
struct WrapP {
using T = boost::callable_traits::class_of_t<decltype(F)>;
using R = boost::callable_traits::return_type_t<decltype(F)>;
T* obj_;
WrapP(T* instance) : obj_(instance) {}
template <typename... Args>
auto operator()(Args... args) const {
return (obj_->*F)(args...);
}
};
It is also possible to extract Args... but it's a bit more cumbersome as you get a std::tuple back.
Related
Is there a way to determine a return type of a member function pointer?
Code sample:
///// my library
void my_func(auto mptr) { // have to use `auto`
// some logic based on a return type of mptr: int, string, A, etc.
}
///// client code
struct A {
int foo();
std::string bar(int);
};
class B{
public:
A func(int, double);
};
// ... and many other classes
my_func(&A::foo);
my_func(&A::bar);
my_func(&B::func);
// ... many other calls of my_func()
I need to "fill in" my_func().
Edit:
I can't use std::result_of/std::invoke_result as I don't know the full list of parameters of mptr. It's not important with which params a method is supposed to be called as I'm not calling it. I would like to avoid creating an object of base class of mptr even if I'm able to determine it (using declval is ok).
You can use partial template specialization to determine the return type of mptr:
template <typename T>
struct ReturnType;
template <typename Object, typename Return, typename... Args>
struct ReturnType<Return (Object::*)(Args...)>
{
using Type = Return;
};
void my_func(auto mptr) {
typename ReturnType<decltype(mptr)>::Type obj;
}
Live Demo
You can write a function that deduces the type of a member function pointer, and returns the deduced return type. Note that only a declaration, and no definition is needed
template <typename C, typename Ret, typename... Args>
auto ret_type(Ret (C::*)(Args...)) -> Ret;
void my_func(auto mptr)
{
using type = decltype(ret_type(mptr));
}
In my opinion, this is also easier to read than the specialization solution.
Here's a demo
You can also account for cv-qualifiers by adding overloads. e.g.
template <typename C, typename Ret, typename... Args>
auto ret_type(Ret (C::*)(Args...) const) -> Ret;
Here's a demo
Trying to pass a lambda function to a template factory function which is templated on the function arguments of the passed function leads gcc-10.2.0 to report no matching function for call to ‘test(main()::<lambda(int, double)>)’.
It does seem to work when I add a + in front of the lambda function forcing the conversion to a function pointer, but I don't see why that would be necessary. Why does the conversion not happen automatically? Is there any way to make this work?
I have also tried std::function<void(TArgs...)> test_func as argument in the declaration of make_test, however that gives me the same no matching function for call error.
#include <iostream>
template <typename... TArgs>
struct test_type {
test_type(void(TArgs...)) {};
};
template <typename... TArgs>
test_type<TArgs...> make_test(void test_func(TArgs...)) {
return test_type<TArgs...>{test_func};
}
int main() {
auto test_object = make_test([](int a, double b) { std::cout << a << b << "\n"; });
return 0;
}
Edit
I was wondering if there maybe is some way to make it work with type traits. Something like the following. Although I know of no way to get the argument list from the template parameter.
template <typename F>
test_type<get_arg_list<F>> make_test(std::function<F>&& f) {
return test_type<get_arg_list<F>>{std::forward(f)};
}
In order to support a variety of callables being passed to your factory (e.g., a stateful lambda or a function pointer), your test_type constructor should accept some kind of type-erased function type like std::function<void(int, double)>:
template<class... TArgs>
struct test_type {
test_type(std::function<void(TArgs...)>) {};
};
Afterwards it's just a matter of writing the boilerplate to handle the following callables being passed to make_test
a regular function pointer
a lambda (struct with a operator()(...) const)
a mutable lambda or a user defined callable without a const operator() function
Here is one approach using type traits:
Start with a base class that we'll specialize for each scenario:
template<class T, class = void>
struct infer_test_type;
(This is a common setup for the voider pattern. We can do this with concepts and constraints, but I'm feeling too lazy to look up the syntax, maybe later)
Regular function pointer specialization
template<class Ret, class... Args>
struct infer_test_type<Ret(*)(Args...)>
{
using type = test_type<Args...>;
};
Now we can write a templated alias for simplicity:
template<class T>
using infer_test_type_t = typename infer_test_type<T>::type;
And we can verify that it works like so:
void foo(int, double){}
// ...
static_assert(std::is_same_v<infer_test_type_t<decltype(&foo)>, test_type<int, double>>);
We can use the type trait for our make_test function like so:
template<class T>
auto make_test(T&& callable) -> infer_test_type_t<T>
{
return infer_test_type_t<T>{std::forward<T>(callable)};
}
Now it's just a matter of covering our other two scenarios.
Callable objects
these will have operator() (either const or not)
I'll start with a top level trait to detect the presence of operator() and feed the type of operator() into another trait.
The top level trait:
// if T is a callable object
template<class T>
struct infer_test_type<T, std::void_t<decltype(&T::operator())>>
{
using type = typename infer_test_type<decltype(&T::operator())>::type;
};
You see that internally it's calling back into another infer_test_type specialization that I haven't shown yet; one that is specialized for operator(). I'll show the two specializations now:
// if operator() is a const member function
template<class T, class Ret, class... Args>
struct infer_test_type<Ret(T::*)(Args...) const>
{
using type = test_type<Args...>;
};
// if operator() is non-const member function
template<class T, class Ret, class... Args>
struct infer_test_type<Ret(T::*)(Args...)>
{
using type = test_type<Args...>;
};
(We could probably combine these two if we wanted to be a little bit smarter and lop off any const at the high level before calling down, but I think this is more clear)
And now we should be able to infer an appropriate test_type for non-generic callables (no generic lambdas or templated operator() functions):
a test with a non-const operator():
struct my_callable
{
void operator()(int, double) // non-const
{
}
};
// ...
static_assert(std::is_same_v<infer_test_type_t<my_callable>, test_type<int, double>>);
And a test with your lambda:
auto lambda = [](int a, double b) { std::cout << a << b << "\n"; };
static_assert(std::is_same_v<infer_test_type_t<decltype(lambda)>, test_type<int, double>>);
Putting it all together
For your simple (non-capturing, non-generic lambda) example it's quite straightforward:
make_test([](int a, double b) { std::cout << a << b << "\n"; });
Demo
Sorry if this case is rather complicated, but I hope it helps people to better understanding modern c++ usage. So I want to make this code works. It should produce special lambdas for single integral type and for variant type for calculate sequence of terms on hard static cast to single type or on soft variant cast to common type, while this terms will be using. I add comments which describes what I really try to do in this code.
#include <variant>
#include <type_traits>
#include <string>
#include <functional>
#include <iostream>
#include <memory>
#include <optional>
#include <any>
#include <utility>
/* Visitor */
// Should get values from a variant
template <class... Ts>
struct Visitor;
template <class T, class... Ts>
struct Visitor<T, Ts...> : T, Visitor<Ts...> {
Visitor(T t, Ts... rest) : T(t), Visitor<Ts...>(rest...) {}
using T::operator();
using Visitor<Ts...>::operator();
};
template <class T>
struct Visitor<T> : T {
Visitor(T t) : T(t) {}
using T::operator();
};
/* Calculator */
// Should get special lambda for same type integrals and for variant packed integrals
template<class T, class... Ts>
class Calculator {
public:
// lambda for simple calculate same type integrals
static auto sum = [](T a, T b) { return a + b; };
// api for get current value of variant
template<typename... Vs>
static auto variant_value(std::variant<Vs...> v) {
return std::visit(Visitor<Vs...>{}, v);
}
// lambda for unpack variant arguments calc common value and pack in variant again
template<typename... Vs>
static auto variant_sum = [](std::variant<Vs...> a, std::variant<Vs...> b) {
auto value = variant_value<Vs...>(a) + variant_value<Vs...>(b);
return std::variant<Vs...>(value);
};
};
/* Term Producer */
namespace term::legion {
using std::function, std::any;
template<typename T>
function<any(any)> legion(auto& algebra) noexcept { // std::function<T(T,T...)
function<any(any)> redex = [](std::any a) {return a;};
// I delete code here because its not important now
return redex;
}
// production lamda for single type values
template<typename T>
std::function<std::any(std::any)> sum(T arg) noexcept {
return legion<T>(Calculator<T>::sum);
};
// production lambda for variant type values
template<typename ...Vs>
std::function<std::any(std::any)> sum() noexcept {
std::cout << "variant sum selected" << std::endl;
return legion<std::variant<Vs...>>(Calculator<Vs...>::template variant_sum<Vs...>);
};
}
int main() {
// term contains lambda for single type
auto sm = term::legion::sum<int>();
// term contains lambda for variant type
auto v_sm = term::legion::sum<char, int16_t, double>();
}
I don't see a way to make your code works...
Anyway, some problems/suggestions
maybe your Visitor works but you can make better using the classic way: variadic inheritance
template <typename ... Ts>
struct Visitor : public Ts...
{ using Ts::operator()...; };
and avoiding specialization and recursion
When you call
auto sm = term::legion::sum<int>();
you have, inside sum() (given that Vs... is int),
Calculator<int>::template variant_sum<int>
that call, inside variant_sum (given that Vs... is int),
variant_value<int>(a) + variant_value<int>(b);
that instantiate, inside variant_value<int>(a) (given that Vs... is int), a
Visitor<int>
The problem is that Visitor inherit from is template paramenters and a class can't inherit from int.
To make a visitor, you need functions that works with all types of the variant but you have to works a little; for example, you can write a sort of operator-wrapper as follows
template <typename T>
struct foo
{ void operator() (T const &) { } };
and a visitor as follows
template <typename ... Ts>
struct Visitor : public foo<Ts>...
{ using foo<Ts>::operator()...; };
This way Visitor<int> inherit from foo<int> (not from int) and foo<int> as a operator() accepting an int.
This works as visitor but you ask
variant_value<Vs...>(a) + variant_value<Vs...>(b);
for the contained value.
And here come the big problem: you can't have the value from std::visit.
Or better... from std::variant<int> (a variant with only a type), you can. You can modify foo<T> as follows
template <typename T>
struct foo
{ T operator() (T const & val) { return val; } };
and (with a couple of little other corrections) this call
auto sm = term::legion::sum<int>();
compile.
But the following sum()
auto v_sm = term::legion::sum<char, int16_t, double>();
because the resulting visitor (Visitor<char, std::int16_t, double>) contains three different operator() functions, returning three different types (char, std::int16_t and double).
But std::visit() must return a single type. Not different types according the type active in the variant.
So I suppose your code has to be completely rethinked.
Other little problems
the following declaration is good in C++20
template<typename T>
function<any(any)> legion(auto& algebra) noexcept
but in C++17 you can't use auto as placeholder for the type of an argument (except for generic lambdas); the correct C++17 way pass through an additional template parameter, so
template <typename T, typename U>
function<std::any(std::any)> legion (U & algebra) noexcept
Add constexpr for sum and variant_sum inside Calculator
static constexpr auto sum = ...
static constexpr auto variant_sum = ...
If Calculator has only public members, you make it a struct.
I'm writing a template function in C++17 which accepts a functor F as argument and I want to restrict the passed in functor to have only one constant reference argument, where T can be any type.
for example:
template <class T> struct my_struct{
std::vector<T> collection;
template <class F> std::vector<T> func(F f){
static_assert(
// CONDITION HERE!,
"f's argument is not const reference"
);
std::vector<T> ret;
std::copy_if(
std::make_move_iterator(this->collection.begin()),
std::make_move_iterator(this->collection.end()),
std::inserter(ret, ret.end()),
f
);
return ret;
}
};
Obviously, in case f is [](auto v){return true;} the resulting vector returned from func will have empty elements (because those are moved before adding to resulting container). So, I need to restrict possible input functors to [](const auto& v){}.
I have tried something like this:
static_assert(
std::is_invocable_v<F, const T&>,
"f's argument is not const reference"
);
But then func([](auto v){}) does not trigger the assertion, because T is copyable in my case.
The func([](auto& v){}) also passes the test, because auto can be const T.
But I need to limit possible lambdas to be func([](const auto& v){}).
You might write traits (with its limitations), something like:
template <typename Sig> struct callable_traits;
template <typename Ret, typename ...Args>
struct callable_traits<Ret(*)(Args...)>
{
using args = std::tuple<Args...>;
};
// add specialization for C-ellipsis too
template <typename Ret, class C, typename ...Args>
struct callable_traits<Ret(C::*)(Args...) const>
{
using args = std::tuple<Args...>;
};
// add specialization for variant with C-ellipsis, cv-qualifier, ref-qualifier
template <class C>
struct callable_traits<C> : callable_traits<&C::operator()>{};
Limitation of the traits: doesn't handle templated operator() (as for generic lambda), overloaded operator().
And then
template <class T> struct my_struct{
template <class F> void func(F f){
static_assert(
std::is_same_v<std::tuple<const T&>, typename callable_traits<F>::args>,
"f's argument is not const reference"
);
// here goes some code which can possibly call f with rvalue
// reference argument, so I want to avoid situation when the
// argument object is moved or modified. I don't have control
// over this code because it an STL algorithm.
}
};
I could be misunderstanding what you're trying to do but, as I read it, you want to accept a callable, then pass some argument to it with a guarantee that the argument cannot be changed (you don't want someone to accept the argument as a non-const l-value reference or an r-value reference. If so, then std::is_invocable should be enough:
#include <type_traits> // for std::is_invocable
#include <functional> // for std::invoke
template <class parameter_t> struct my_struct {
template <typename callable_t>
void function(callable_t const &callable) requires (
std::is_invocable<callable_t, parameter_t const &>::value
) {
// . . .
std::invoke(callable, parameter_t{});
// . . .
}
};
Then:
int main() {
my_struct<int>{}.function([](int const&){}); // Fine
my_struct<int>{}.function([](int){}); // Fine, a copy of the parameter is made when the lambda is invoked.
my_struct<int>{}.function([](int &){}); // error: no matching member function for call to 'function'
my_struct<int>{}.function([](int &&){}); // error: no matching member function for call to 'function'
}
(You can play around with it here)
A possible problem is that this method does allow a copy to be made, but if the main goal is to protect the variable you hold from changes this should be good enough.
P. S. I know I used c++20 requires clause as a way of future-proofing the answer, but it should be trivial to convert it to if constexpr, static_assert or any other way you prefer.
I finally managed to achieve it in the following way:
template <class T> struct my_struct{
std::vector<T> collection;
struct noncopyable_value_type : T{
noncopyable_value_type(const noncopyable_value_type&) = delete;
noncopyable_value_type& operator=(const noncopyable_value_type&) = delete;
};
template <class F> std::vector<T> func(F f){
static_assert(
std::is_invocable_v<F, noncopyable_value_type>,
"f's argument must be const reference"
);
std::vector<T> ret;
std::copy_if(
std::make_move_iterator(this->collection.begin()),
std::make_move_iterator(this->collection.end()),
std::inserter(ret, ret.end()),
f
);
return ret;
}
};
But still, the problem here is that it only works with generic lambdas.
I am writing a kind of container class, for which I would like to offer an apply method which evaluates a function on the content of the container.
template<typename T>
struct Foo
{
T val;
/** apply a free function */
template<typename U> Foo<U> apply(U(*fun)(const T&))
{
return Foo<U>(fun(val));
}
/** apply a member function */
template<typename U> Foo<U> apply(U (T::*fun)() const)
{
return Foo<U>((val.*fun)());
}
};
struct Bar{};
template class Foo<Bar>; // this compiles
//template class Foo<int>; // this produces an error
The last line yields error: creating pointer to member function of non-class type ‘const int’. Even though I only instantiated Foo and not used apply at all. So my question is: How can I effectively remove the second overload whenever T is a non-class type?
Note: I also tried having only one overload taking a std::function<U(const T&)>. This kinda works, because both function-pointers and member-function-pointers can be converted to std::function, but this approach effectively disables template deduction for U which makes user-code less readable.
Using std::invoke instead helps, it is much easier to implement and read
template<typename T>
struct Foo
{
T val;
template<typename U> auto apply(U&& fun)
{
return Foo<std::invoke_result_t<U, T>>{std::invoke(std::forward<U>(fun), val)};
}
};
struct Bar{};
template class Foo<Bar>;
template class Foo<int>;
However, this won't compile if the functions are overloaded
int f();
double f(const Bar&);
Foo<Bar>{}.apply(f); // Doesn't compile
The way around that is to use functors instead
Foo<Bar>{}.apply([](auto&& bar) -> decltype(auto) { return f(decltype(bar)(bar)); });
Which also makes it more consistent with member function calls
Foo<Bar>{}.apply([](auto&& bar) -> decltype(auto) { return decltype(bar)(bar).f(); });
In order to remove the second overload you'd need to make it a template and let SFINAE work, e. g. like this:
template<typename T>
struct Foo
{
T val;
//...
/** apply a member function */
template<typename U, typename ObjT>
Foo<U> apply(U (ObjT::*fun)() const)
{
return Foo<U>((val.*fun)());
}
};
Alternatively, you could remove the second overload altogether, and use lambda or std::bind:
#include <functional> // for std::bind
template<typename T>
struct Foo
{
T val;
/** apply a member function */
template<typename U, typename FuncT>
Foo<U> apply(FuncT&& f)
{
return {f(val)};
}
};
struct SomeType
{
int getFive() { return 5; }
};
int main()
{
Foo<SomeType> obj;
obj.apply<int>(std::bind(&SomeType::getFive, std::placeholders::_1));
obj.apply<int>([](SomeType& obj) { return obj.getFive(); });
}
How can I effectively remove the second overload whenever T is a non-class type?
If you can use at least C++11 (and if you tried std::function I suppose you can use it), you can use SFINAE with std::enable_if
template <typename U, typename V>
typename std::enable_if<std::is_class<V>{}
&& std::is_same<V, T>{}, Foo<U>>::type
apply(U (V::*fun)() const)
{ return Foo<U>((val.*fun)()); }
to impose that T is a class.
Observe that you can't check directly T, that is a template parameter of the class, but you have to pass through a V type, a template type of the specific method.
But you can also impose that T and V are the same type (&& std::is_same<V, T>{}).