Is there any way to deduce the return type of a member function from within itself at compile time?

Started by
5 comments, last by Shaarigan 6 years, 2 months ago

I need to implement really simple event forwarding and I want to avoid two things:

1) having the user type out the name of the current class (or any class for that matter) and
2) having to worry about the return value, which may be void

The forwarding itself will be wrapped into something like:

bool/int/void MyCustomHandler::OnMouseEvent(int e, int, x, int y)
{
FORWARD_EVENT(OnMouseEvent, e, x, y); // calls some default implementation for this event
}

I want FORWARD_EVENT everywhere as long as it is given the name of the function and arguments.

Of the the two problems listed above I have (2) under control, and I can also deduce the return value for (1) given a function signature. But I can't figure out if there's a way to automatically deduce the signature. Basically I want the compile time equivalent of this:


using t2 = decltype((*this).OnMouseEvent);

I've butted heads with member functions before so this has bugged me in the past. So far, as best I can tell there's no real way to accomplish this. I hope I'm wrong :)

Advertisement

you can use this method:

Stack Overflow Variable Type

This will give you the possibility to check the type of a variable with just a simple compare (switch, if, or similar)

I would be aware of this solution(s) because compiler may claim that there are static constants in your code and code will be unreachable. If you really need to behave different just make a template and put specialisations for it that fit your needs in a way you like.

I set my delegate system ontop of something like this that will handle any signature for me


template<typename ret DO_IF(ARGS, SEPARATOR) VARIADIC_PARAMS_ARGS(typename Args, ARGS)> struct StaticContext<ret (VARIADIC_PARAMS_ARGS(Args, ARGS))>
{
	public:
		typedef ret ReturnValue;
		typedef ret (*Signature) (VARIADIC_PARAMS_ARGS(Args, ARGS));

		...
  
		inline static int ParameterCount() { return ARGS; }

		Signature Function;
};
template<VARIADIC_PARAMS_ARGS(typename Args, ARGS)> struct StaticContext<void (VARIADIC_PARAMS_ARGS(Args, ARGS))>
{
	public:
		typedef void ReturnValue;
		typedef void (*Signature) (VARIADIC_PARAMS_ARGS(Args, ARGS));

		...

		inline static int ParameterCount() { return ARGS; }

		Signature Function;
};

Which is a header file that will be included like this into another header file


template<typename signature> struct StaticContext;

#define ARGS 0
#include <Delegate/StaticContext.Partial.h>
#undef ARGS

#define ARGS 1
#include <Delegate/StaticContext.Partial.h>
#undef ARGS

And enables my static context to look like this


StaticContext<void (int, int)> mouseEvent;

 

In c++11 you wont need that macros but instead use variadic templates for this

23 hours ago, Revan1985 said:

you can use this method:

Stack Overflow Variable Type

This will give you the possibility to check the type of a variable with just a simple compare (switch, if, or similar)

That thread deals with RTTI. I need to accomplish this at compile time.

21 minutes ago, Shaarigan said:

... snip ...

 

I already have the code down that deals with signature extraction in very much the same way. I ended up having a little bit of trouble getting it to work with constant member functions, though. Anyway, here's the solution I came up with yesterday. I haven't tested it thoroughly, but it does seem to work.

A short rundown of what the code does: 
 - first, remove const from member functions. I don't know why but, return type deduction does not work when const is present and reports the decltype to be an incomplete type. Perhaps someone can fill me in here?
 - next, use really simple manual return type deduction. 
 - as far as I can tell, there is no way to deduce the current class at compile time without previously noting it down somewhere. This is fine in my case as I'm writing a kind of a plugin system, which requires the user to implement a default interface with something like IMPLEMENT_EXTENSION(...classnamehere...) in the main body of the class anyway. Nevertheless, here the solution is to simply typedef the class somewhere near the top of the class declaration.

Here's a short test snippet that works in VS2013. It automatically forwards any call to the base class, but it's trivial to make it do anything.

 


// return type deduction from a member function
template<class T>
struct												return_type;

template<class C, class R, class... Args>
struct return_type<R(C::*)(Args...)>				{ using type = R; };


// const removal from a member function
template <typename T>
struct												function_remove_const;

template <typename R, typename C, typename... Args>
struct function_remove_const<R(C::*)(Args...)>		{ using type = R(C::*)(Args...); };

template <typename R, typename C, typename... Args>
struct function_remove_const<R(C::*)(Args...)const>	{ using type = R(C::*)(Args...); };

// just to hide the local clutter
#define FORWARD_EVENT(_fn, ...)														\
    using _fn##_FUNTYPE = function_remove_const<decltype(&CLASS_TYPE::_fn)>::type;	\
    using _fn##_RETTYPE = return_type<_fn##_FUNTYPE>::type;							\
    return _fn##_RETTYPE(this->TESTBASE::_fn(__VA_ARGS__));


class TESTBASE {
    public:
        virtual
        void voidfn() const {
        }

        virtual
        bool boolfn(int32 arg) const {
            return arg == 1;
        }
};


class TESTCLASS
    : public TESTBASE {
    public:
        // need this in FORWARD_EVENT()
        using CLASS_TYPE			= TESTCLASS;

        void voidfn() const override {
            // returning void is not an issue
            FORWARD_EVENT(voidfn);
        }

        bool boolfn(int32 arg) const override {
            FORWARD_EVENT(boolfn, arg);
        }
};

I found nothing of this sort on the web, so hopefully someone finds this useful.

I kept the const-ness of my delegates by offering different binding functions because of two reasons

  • User may need to know if the function bound to a delegate is const
  • Const/Non-Const overloads wont work together when using an automated template deduction instead of offering two binding functions

Just if someone would get in trouble with that ;)

This topic is closed to new replies.

Advertisement