How can I get the class of a member function pointer? - c++

Consider the code:
class Character
{
void kill();
void send_to_wall();
}
template <typename T>
void GeorgeFunc(T fp)
{
??? obj;
(obj.*fp)();
}
int main()
{
GeorgeFunc(&Character::kill);
}
So my question here is: how can I get ???? It seems that the compiler definitely knows what this type is (Character) during template instantiation, but I'm not sure how to get at it. My current workaround is to change to: void GeorgeFunc(void (T::*fp)()), but it would be cleaner to simply get the type from the member function pointer. decltype(fp) would return void(Character::*)(), and decltype(fp()) would return void. Any way to get Character?

Yes, just use a trait to determine this.
template <typename> struct member_function_traits;
template <typename Return, typename Object, typename... Args>
struct member_function_traits<Return (Object::*)(Args...)>
{
typedef Return return_type;
typedef Object instance_type;
typedef Object & instance_reference;
// Can mess with Args... if you need to, for example:
static constexpr size_t argument_count = sizeof...(Args);
};
// If you intend to support const member functions you need another specialization.
template <typename Return, typename Object, typename... Args>
struct member_function_traits<Return (Object::*)(Args...) const>
{
typedef Return return_type;
typedef Object instance_type;
typedef Object const & instance_reference;
// Can mess with Args... if you need to, for example:
static constexpr size_t argument_count = sizeof...(Args);
};
Now your declaration is:
typename member_function_traits<T>::instance_type obj;
However, I would argue that since you require a member function pointer (other types would fail to instantiate due to the line (obj.*fp)()1) that your function should take a member function pointer directly instead of a completely generic type.
So this definition would not only work, but I would consider it preferred -- the error messages when someone uses something other than a pointer-to-member-function will be much clearer because the argument type will be incompatible:
template <typename Return, typename Object>
void GeorgeFunc(Return (Object::*fp)())
{
Object obj;
(obj.*fp)();
}
Note that this does allow passing a pointer-to-member-function that returns any type. Since we don't really use the return value, we don't care what it is. There is no reason to enforce that it is void as in your "workaround."
The only downside to using this approach is that you need two overloads if you intend to also accept pointers to member functions that are declared const. The completely generic implementation does not have this limitation. (I've long wished that pointers to const member functions were implicitly convertible to pointers to non-const member functions, but this is currently not allowed by C++.)
1 This isn't 100% true. If you use a completely generic type as you are right now then the caller could theoretically pass a member data pointer instead of a member function pointer. obj.*fp would evaluate as a reference to the data member, and then you would be invoking operator()() on it. As long as the data member's type implemented this operator then the template function GeorgeFunc could be instantiated.

Related

Conversion using std::decay_t

I have some code but I do not understand what does it do
template <typename T, typename U = T>
struct MyStruct
{
};
template <typename T>
[[nodiscard]] inline T fromValue(const QJsonValue& json)
{
return MyStruct<std::decay_t<T>>::get(json);
}
I do not understand what heppenes in line
return MyStruct<std::decay_t<T>>::get(json);
And Why we use empty struct?
You use std::decay_t in order to obtain the basic type without cv-qualification (plus some fewer used special features for arrays and function pointers). Starting from C++20, for most cases it should be sufficient to use std::remove_cvref_t<T> for this.
The function signature
template <typename T>
[[nodiscard]] inline T fromValue(const QJsonValue& json)
{
return MyStruct<std::decay_t<T>>::get(json);
}
basically tells you, that regardless of the cv-qualification (T, T&, T const&, T volatile const&, etc.) the class template should be instantiated only with the base type T. Thereafter, the static function get is called -- static because those are functions that can be called without an object.
EDIT: sorry, I just recognized that the struct is empty. So your code won't work, as there simply is no get function available.
Note however, that there is a danger of returning a dangling reference here, because for T& as template parameter, you return a reference. Moreover, you don't want consts, volatiles, etc. in your return type. Therefore I would question the design of this function and use it with caution.

What does T::* mean in template's parameters?

Following the article written in here:
I came across this code (shortened and changed for clarity):
template <class T> struct hasSerialize
{
// This helper struct permits us to check that serialize is truly a method.
// The second argument must be of the type of the first.
// For instance reallyHas<int, 10> would be substituted by reallyHas<int, int 10> and works!
// reallyHas<int, &C::serialize> would be substituted by reallyHas<int, int &C::serialize> and fail!
// Note: It only works with integral constants and pointers (so function pointers work).
// In our case we check that &C::serialize has the same signature as the first argument!
// reallyHas<std::string (C::*)(), &C::serialize> should be substituted by
// reallyHas<std::string (C::*)(), std::string (C::*)() &C::serialize> and work!
template <typename U, U u> struct reallyHas;
// We accept a pointer to our helper struct, in order to avoid to instantiate a real instance of this type.
// std::string (C::*)() is function pointer declaration.
template <typename C>
static char&
test(reallyHas<std::string (C::*)(), &C::serialize>* /*unused*/) { }
};
So this
std::string (C::*)()
caught my attention.
Can anyone explain me the C::* part? That is a function pointer that returns std::string but what more?
A member function pointer to a member in class C that returns a std::string.
Check isocpp.org for more info on pointers to member functions.

Compile time deduction of template member function

Is it possible to get the return type of a template member function at compile time?
I guess I need something along the lines of:
template<class T>
struct SomeClass
{
// T must have a function foo(int), but do not know the
// return type, it could be anything
using RType = ??? T::foo(int) ???; // Is it possible to deduce it here?
}
What you want to do can be achieved by using the decltype operator together with the std::declval template.
decltype(EXPRESSION) yields – at compile time – the type that EXPRESSION would have. The EXPRESSION itself is never evaluated. This is much like sizeof(EXPRESSION) returns the size of whatever EXPRESSION evaluates to without ever actually evaluating it.
There is only one problem: Your foo is a non-static member function so writing decltype(T::foo(1)) is an error. We somehow need to obtain an instance of T. Even if we know nothing about its constructor, we can use std::declval to obtain a reference to an instance of it. This is a purely compile-time thing. std::declval is actually never defined (only declared) so don't attempt to evaluate it at run-time.
Here is how it would look together.
#include <type_traits>
template <typename SomeT>
struct Something
{
using RetT = decltype(std::declval<SomeT>().foo(1));
};
To see that it actually works, consider this example.
struct Bar
{
float
foo(int);
};
struct Baz
{
void
foo(int);
};
int
main()
{
static_assert(std::is_same<float, Something<Bar>::RetT>::value, "");
static_assert(std::is_same<void, Something<Baz>::RetT>::value, "");
}
While this does what I think you have asked for, it is not ideal in the sense that if you attempt to instantiate Something<T> with a T that doesn't have an appropriate foo member, you'll get a hard compiler error. It would be better to move the type computation into the template arguments such that you can benefit from the SFINAE rule.
template <typename SomeT,
typename RetT = decltype(std::declval<SomeT>().foo(1))>
struct Something
{
// Can use RetT here ...
};
If you know the argument types to your function call the following should work:
template<typename T>
struct X
{
typedef typename decltype(std::declval<T>.foo(std::declval<int>())) type;
};
If you don't you can still deduce the type of the function pointer and extract the return type:
template<class F>
struct return_type;
template<class C, class R, class... Args>
struct return_type<R(C::*)(Args...)>
{ using type = R; };
template<typename T>
struct X
{
typedef typename return_type<decltype(&T::foo)>::type type;
};
This will fail if T::foo is an overloaded function or member of T.
Unfortunately it is only possible to know the return type of some expression if you know with what arguments you are going to call it (which, unfortunately, often is a different place from where you need to know the return type)...

How to create boost::function from template signature

Recently I was trying to create flexible observer pattern implementation which hides boost::signal. I almost succeeded.
I have a Observer class which has to have an update method matching signature provided by template parameter.
Example of use:
Observable<void(float, float)> observable;
Observer<void(float, float)> observer;
observable.attach(&observer);
observable.notify(Observable::Arguments(10.0f, 1.0f)); // invokes observer->update(10.0f, 1.0f);
Everything works just fine if observer does not have overloaded update method. In that case boost::bind can not deduce correct method to use. Unfortunately I can't use explicit casting because I don't know update arguments (this information is in FunctionSignature).
Following method causes troubles:
class Observable <typename FunctionSignature>
{
...
template <class DerivedObserverClass>
void attach(DerivedObserverClass* observer)
{
STATIC_ASSERT((boost::is_base_of<ObserverType, DerivedObserverClass>::value));
ConnectionsMap::iterator it = connections.find(observer);
if (it == connections.end() || !it->second.connected()) {
// i would like to do something like
// boost::function<FunctionSignature> f;
// f = boost::bind(&static_cast<FunctionSignature>DerivedObserverClass::update, observer, _1);
// singnalSlot is defined as boost::signal<FunctionSignature>
// this works as long, as Derived class doesn't have overloaded update method
connections[observer] = signalSlot.connect(boost::bind(&DerivedClass::update, observer, _1));
} else {
throw std::invalid_argument("Observer already attached.");
}
}
I think that boost::function could help to solve this problem. I don't know how to bind it with correct member method using only template signature.
Is it even possible?
No, boost::function won't help you either. 13.4.3 says
Nonstatic member functions match targets of type
“pointer-to-member-function;” the function type of the pointer to
member is used to select the member function from the set of
overloaded member functions.
This means you cannot take an address of overloaded member function, pass it to any kind of function object (templated or not, boost or std or whatever), and hope the overloading will resolve itself. You need a genuine honest pointer-to-member-function type at the left-hand side of the assignment.
You will have to convert your FunctionSignature to a pointer-to-member-function type somehow. Here's some old-fashioned template magic that does what you need, for a limited number of function arguments. c++0x might have a better, more general solution.
template <typename C, typename F>
struct tomemfun;
template <typename C, typename res>
struct tomemfun<C, res()>
{
typedef res (C::*memfun_t)();
};
template <typename C, typename res, typename arg1>
struct tomemfun<C, res(arg1)>
{
typedef res (C::*memfun_t)(arg1);
};
template <typename C, typename res, typename arg1, typename arg2>
struct tomemfun<C, res(arg1, arg2)>
{
typedef res (C::*memfun_t)(arg1, arg2);
};
// repeat with more arguments as needed
Now you can use
tomemfun<DerivedClass, FunctionSignature>::memfun_t update = &DerivedClass::update;
and it will resolve to the right overloaded function.
boost might already have such a conversion template, but I couldn't find it.

does sfinae instantiates a function body?

I want to detect existence of a specific member function for a class, using the usual SFINAE trick.
template<typename T>
struct has_alloc
{
template<typename U,U x>
struct dummy;
template<typename U>
static char test(dummy<void* (U::*)(std::size_t),&U::allocate>*);
template<typename U>
static char (&test(...))[2];
static bool const value = sizeof(test<T>(0)) ==1;
};
It should be noted that this detects a different kind of allocator which has void* allocate(std::size_t) as member function which are non standard (probably some raw memory allocator).
Next, I have an incomplete type and an std::allocator for that incomplete type.
struct test;
typedef std::allocator<test> test_alloc;
And I am checking whether the test_alloc is the one I am looking for.
struct kind_of_alloc
{
const static bool value = has_alloc<test_alloc>::value;
};
Surely struct test will be complete when I will "use" test_alloc such as
#include "test_def.hpp"//contains the code posted above
struct test{};
void use()
{
test_alloc a;
}
in another compilation unit. However when the has_alloc test happens,the compiler tries to instantiate the allocate function for std::allocator and finds that sizeof an incomplete type is used inside function body, and causes a hard error.
It seems that the error doesn't occur if the implementation of allocate separated and included separately at the point of use such as
template<typename T>
T* alloc<T>::allocate(std::size_t n)
{
return (T*)operator new(sizeof(T)*n);
}
void use()
{
test_alloc a;
a.allocate(2);
}
And test_def.hpp contains
template<typename T>
struct alloc
{
T* allocate(std::size_t n);
};
However while I can do this for alloc<T> , it is not possible for std::allocator as separating out the implementation is not possible.
What I am looking for is it possible to test whether a function with void* allocate(size_t) exists in test_alloc. If not, it will test negative, and if yes ,i.e. if the function signature matches, even if it can not be instantiated there, test positive.
No, SFINAE is only in effect during overload resolution. Once a resolution has been made and the compiler begins instantiating the function SFINAE is over.
Edit: and taking the address of a function instantiates it.