Given a class
class Foo {
public:
std::shared_ptr<const Bar> quux(const std::string&, std::uint32_t);
}
I can declare an std::function that has the same interface:
std::function<std::shared_ptr<const Bar>(const std::string&, std::uint32_t)> baz = ...
Is there a way of compressing that declaration such that the template arguments to std::function are derived from the declaration of that method, something like:
std::function<functype(X::quux)> baz = ...
where functype is an imaginary C++ operator similar to decltype. Is there a way to do this / does c++ have such a capability?
I do see that the method has a slightly different signature actually as it would also take a reference/pointer to the this object; it would be fine for me to derive such a signature too.
Yes, you can. Adapting How do I get the argument types of a function pointer in a variadic template class? to your request, we get:
template<typename T>
struct function_traits;
template<typename R, typename C, typename ...Args>
struct function_traits<R(C::*)(Args...)>
{
using type = std::function<R(Args...)>;
};
class Bar;
class Foo {
public:
std::shared_ptr<const Bar> quux(const std::string&, std::uint32_t);
};
int main()
{
std::cout << std::is_same<
std::function<std::shared_ptr<const Bar>(const std::string&, std::uint32_t)>,
function_traits<decltype(&Foo::quux)>::type>::value << std::endl;
}
To make it work with constant methods you will need another specialization:
template<typename R, typename C, typename ...Args>
struct function_traits<R(C::*)(Args...) const>
{
using type = std::function<R(Args...)>;
};
But you will get problems with overloaded methods, because in order to resolve overloading you will need to specify the arguments anyway.
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
I am looking for a way to extract the types of an std::tuple to define a method signature. Take the following (contrived) example:
template <typename RetT, typename... ArgsT>
class A
{
public:
typedef RetT ReturnType;
typedef std::tuple<ArgsT...> ArgTypes;
RetT doSomething(ArgsT... args)
{
// Doesn't make much sense, but it's just an example
return (RetT) printf(args...);
}
};
template <typename Enable, typename RetT, typename... ArgsT>
class AAdapter;
// Simply pass arguments along as-is
template <typename RetT, typename... ArgsT>
class AAdapter<std::enable_if_t<!std::is_same_v<RetT, float>>, RetT, ArgsT...> : public A<RetT, ArgsT...> {};
// Add additional first argument if RetT is float
template <typename RetT, typename... ArgsT>
class AAdapter<std::enable_if_t<std::is_same_v<RetT, float>>, RetT, ArgsT...> : public A<RetT, const char*, ArgsT...> {};
template <typename RetT, typename... ArgsT>
class B
{
public:
typedef AAdapter<void, RetT, ArgsT...> AAdapter;
// This needs to have the same method signature (return type and argument types) as AAdapter::doSomething()
template <size_t... Index>
typename AAdapter::ReturnType doSomething (
typename std::tuple_element<Index, typename AAdapter::ArgTypes>::type... args
) {
return a.doSomething(args...);
}
public:
AAdapter a;
};
int main(int argc, char** argv)
{
// I would like to be able to remove the <0,1,2> and <0,1,2,3> below.
B<int, const char*, int, int> b1;
b1.doSomething<0,1,2>("Two values: %d, %d\n", 1, 2);
B<float, const char*, int, int> b2;
b2.doSomething<0,1,2,3>("Three values: %s, %d, %d\n", "a string", 1, 2);
return 0;
}
Consider the way in which AAdapter changes, adds or removes argument types opaque. Basically, I want B::doSomething() to simply redirect to B::AAdapter::doSomething(), so I want both of these methods to have the exact same signature. The question is: How do I get the argument types of B::AAdapter::doSomething() from inside B?
My definition of B::doSomething() in the code above is the furthest I have come: I'm typedef'ing an std::tuple with the argument types inside A, so I can unpack them back to a parameter pack in B. Unfortunately, with the approach above I still need to provide the Index... template parameters manually when calling B::doSomething(). Surely there must be a way to have these Index... parameters automatically deduced from the size of the tuple. I have thought about approaches using std::make_integer_sequence, but that would require me to define an additional method argument for the sequence itself (and it can't be the last argument with a default value because no other arguments are allowed after a parameter pack).
Is there any way I can do this, with or without std::tuple? Solutions that require C++17 will be fine.
EDIT 1:
I realize now that I could probably circumvent the problem in my particular application by having B inherit from AAdapter instead of having an AAdapter object as a member, but I would still like to know how to solve the problem without having to do that.
EDIT 2:
Maybe some additional info on why AAdapter exists and what I want to achieve. I am implementing a kind of wrapper class around an existing C API that actually needs to be called in another process, RPC-style. So if the user wants to call a C function in the remote process, they will instead call a corresponding method in my wrapper class locally that handles all the RPC stuff like type conversions, the actual remote call and other ugly details. This wrapper class is represented by B in my code above. Now my wrapper method signature will usually not have the exact same signature as the C function. For example, the wrapper may have std::string_view instead of a pair of const char*, size_t that the C function has. For reasons that are not important here, it also needs to have an output parameter (a pointer) where the C function has a return value instead sometimes.
In order for me to not have to define two separate method signatures (in actuality it is three) and write code to convert the parameters for every single one, I instead pass only one of the signatures as template parameters RetT, ArgsT... to B. A signature conversion class (AAdapter in the example above) then applies rules for how to generate the second signature automatically from this first one by adding parameters, changing their types, etc.. A would then hold this generated signature, and B would have the one I provided initially. However, I want B to provide an invoke() method with the signature of A, thus hiding A and the entire method signature mess from the user completely. This is why I need access to the template parameter types of A from within B, and why I can't simply remove the middle class AAdapter.
The core of your problem is turning a tuple into an argument pack.
maybe the tuple type is not the template arguments? in this case, there is a simple solution by inheritance:
#include <vector>
#include <iostream>
#include <tuple>
template<typename... Types>
struct BImpl{
typedef std::tuple<std::vector<Types>...> tuple_type;
// maybe you will get a tuple type from some class templates. assume the 'tuple_type' is the result.
// requirement: 'tuple_type' = std::tuple<SomeTypes...>
// requirement: 'tuple_type' can be deduced definitely from template arguments 'Types...'.
template<typename> // you can add other template arguments, even another std::tuple.
struct OptCallHelper;
template<typename... Args>
struct OptCallHelper<std::tuple<Args...>>{
auto dosomething(Args&&... args) /* const? noexcept? */{
// do what you want...
// requirement: you can definitely define the 'dosomething' here without any other informations.
std::cout << "implement it here." << std::endl;
}
};
typedef OptCallHelper<tuple_type> OptCall;
};
template<typename... Types>
struct B : private BImpl<Types...>::OptCall{
typedef typename BImpl<Types...>::OptCall base;
using base::dosomething;
// obviously, you can't change the implementation here.
// in other words, the definition of 'dosomething' can only depend on template arguments 'Types...'.
};
int main(){
B<int, float> b;
b({}, {}); // shows "implement it here."
return 0;
}
you can do what you want to do in BImpl and then use B instead.
// This needs to have the same method signature (return type and argument types) as AAdapter::doSomething()
template <size_t... Index>
typename AAdapter::ReturnType doSomething (
typename std::tuple_element<Index, typename AAdapter::ArgTypes>::type... args
) {
return a.doSomething(args...);
}
for AAdaptor, I think you just want the interface of dosomething in A, and you can deduce it:
#include <iostream>
template<typename...>
struct AAdaptor{
int dosomething(){
std::cout << "???" << std::endl;
return 0;
}
};
// ignore the implementation of AAdaptor and A.
// just consider of how to get the interface of 'dosomething'.
template<typename... Types>
struct BImpl{
typedef AAdaptor<Types...> value_type;
typedef decltype(&value_type::dosomething) function_type;
// attention: it won't work if 'AAdaptor::dosomething' is function template or overloaded.
// in this case, you should let A or AAdaptor give a lot of tuples to declare 'dosomething', referring to the first solution.
template<typename>
struct OptCallHelper;
template<typename Ret, typename Klass, typename... Args>
struct OptCallHelper<Ret(Klass::*)(Args...)>{
value_type data;
Ret dosomething(Args... args){
return data.dosomething(args...);
}
};
// attention: 'Ret(Klass::*)(Args...)' is different from 'Ret(Klass::*)(Args...) const', 'noexcept' as well in C++17.
// even Ret(Klass::*)(Args..., ...) is also different from them.
// you have to specialize all of them.
typedef OptCallHelper<function_type> OptCall;
};
template<typename... Types>
struct B : BImpl<Types...>::OptCall{
typedef typename BImpl<Types...>::OptCall base;
using base::dosomething;
};
int main(){
B<int, float> b;
b(); // shows "???"
return 0;
}
if there is some difference between this code and your requirement, try to give another example to imply some of your implementation. it's still not clear what B gets and should do.
This demonstrates how you can get a function with the argument types from a tuple:
#include <iostream>
#include <tuple>
#include <utility>
template <
typename ArgTuple
>
class B_Class {};
template <typename... ArgTypes>
class B_Class<std::tuple<ArgTypes...> > {
public:
static void b(
ArgTypes...
) {
std::cout << "successful call" << std::endl;
}
};
int main() {
using ArgTypes = std::tuple<int, char, float, double>;
int i; char c; float f; double d;
B_Class<ArgTypes>::b(i, c, f, d);
}
This compiles and prints "successful call" when run.
For example
template<class T, class U>
void f();
template<class T> using g = f<T, int>;
Or any similar idea for functions?
No. You cannot do that. You need to create a new function that calls f, forwarding all arguments and template arguments.
template<class T, class U>
void f();
template<class T>
void g() {
f<T, int>();
}
A C++14 alternative is a variable template of function pointer type:
template<typename T>
void (*g)() = &f<T, int>;
Although this approach ignores default arguments and probably has other quirks as well. I highly recommend the more verbose wrapping approach.
No, you can not do that as templated aliases are used to create aliases of types, not concrete data.
What you are trying to do is to create an alias/type from the address of a function as f<T, int> decays into a pointer to function.
You can however create a templated alias from the type of the function f.
template <typename T>
using g = typename std::add_pointer<decltype(f<T, int>)>::type;
int main() {
// Type of 'func' is 'void (*)()' with template arguments '{T1=double, T2=int}'.
g<double> func;
func = f<double, int>; // Point to function with same signature and template args.
func(); // Ok to call function.
}
In templates as shown below, I would like the call Run(&Base::foo) succeed without the need to name the Base type twice (as is done in the compiling Run<Base>(&Base::foo) call). Can I have that? Possibly without adding a ton of Boost headers?
With the provided code, I get an error of:
prog.cpp:26: error: no matching function for call to ‘Run(bool (Base::*)())’
(you can fiddle with the snippet at http://ideone.com/8NZkq):
#include <iostream>
class Base {
public:
bool foo() { return true; }
};
Base* x;
template<typename T>
struct Traits {
typedef bool (T::*BoolMethodPtr)();
};
template<typename T>
void Run(typename Traits<T>::BoolMethodPtr check) {
T* y = dynamic_cast<T*>(x);
std::cout << (y->*check)();
}
int main() {
Base y;
x = &y;
Run<Base>(&Base::foo);
Run(&Base::foo); // why error?
}
The T in Traits<T>::BoolMethodPtr is in a non-deduced context, so the compiler will not deduce automatically from the call what type T should be.
This is because there could be code like this:
template<typename T>
struct Traits {
typedef bool (T::*BoolMethodPtr)();
};
template<>
struct Traits<int> {
typedef bool (Base::*BoolMethodPtr)();
};
Run(&Base::foo); /* What should T be deduced to? Base and int are both equally possible */
If you can do without the Traits<T> class, you can write Run as:
template<class Class>
void Run(bool (Class::*check)()) {
Class* y = dynamic_cast<Class*>(x);
std::cout << (y->*check)();
}
In this context, Class can be deduced to mean Base
To pick apart a type, any type, use partial specialization. There is no function template partial specialization, so you'll need to directly parameterize the function on its argument type and retrieve the class type inside.
template< typename T >
struct get_host_class; // most types are not ptmfs: don't implement this
template< typename C >
struct get_host_class< bool (C::*)() > { // implement partial specialization
typedef C host;
typedef void sfinae; // disallow function for non ptmf arguments
};
template< typename T >
typename get_host_class<T>::sfinae Run( T check) {
typedef T BoolMethodPtr; // or something
typedef typename get_host_class< T >::host host;
}
I think this is a non deduced context.
$14.8.2.5/5- "The non-deduced contexts
are: — The nested-name-specifier of a
type that was specified using a
qualified-id."
I think this is the quote that applies in this case. But some template gods need to ratify my understanding.
When the compiler tries to match a template argument, it only considers the primary class type. In other words, when it encounters the expression:
Run(&Base::foo);
...and it's trying to figure out the template parameter for Run, it only considers the type of foo itself, and doesn't consider whatever class foo is a part of.
EDIT:
And the type of foo is bool(Base::*)(void), but what you want the compiler to find is just Base