• Advertisement
Sign in to follow this  

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

Recommended Posts

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 :)

Share this post


Link to post
Share on other sites
Advertisement

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

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites

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 ;)

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this  

  • Advertisement
  • Advertisement
  • Popular Tags

  • Advertisement
  • Popular Now

  • Similar Content

    • By mangine
      Hello. I am developing a civ 6 clone set in space and I have a few issues. I am using Lua for the logic and UI of the game and c++ directx 12 for the graphics. I need a way to send information between Lua and c++ occasionally and was wondering what is the best and most flexible (and hopefully fast) way to do this. Don't forget that I also need to send things from c++ back to Lua. I know of a lua extension called "LuaBridge" on github but it is a little old and I am worried that it will not work with directx 12. Has anybody done something similar and knows a good method of sending data back and forth? I am aware that Lua is used more and more in the industry and surely plenty of AAA game programmers know the answer to this. I want a good solution that will hopefully still be viable code in a couple of years...
    • By owenjr
      Hi there.
      I'm pretty new to this and I don't know if it has been asked before, but here I go.
      I'm developing a game using SFML and C++.
      I would like to use the "Tiled" tool to load maps into my game but I don't actually find any tutorial or guide on how to exaclty use it (I know that I have to read an XML file and stuff). I just step into diverse projects that make all a mess. 
      Anyone knows where can I find good information to make my map loader by myself?
      Thanks in advantage!!
    • By MHG OstryTM
      Hello guys,
      I've released my game for the first time. I'm very excited about it and I hope you'll enjoy the game - Beer Ranger. It's a retro-like puzzle-platfromer which makes you think a lot or die trying. You have a squad of skilled dwarfs with special powers and your goal is tasty beer. There is a lot of traps as well as many solutions how to endure them - it is up to your choice how to complete the level! 
      Link to the project: Project site
      Link to the Steam site with video: Beer Ranger
      Have fun and please write feedback if you feel up to.
      Some screens: 




    • By francoisdiy
      So I wrote a programming language called C-Lesh to program games for my game maker Platformisis. It is a scripting language which tiles into the JavaScript game engine via a memory mapper using memory mapped I/O. Currently, I am porting the language as a standalone interpreter to be able to run on the PC and possibly other devices excluding the phone. The interpreter is being written in C++ so for those of you who are C++ fans you can see the different components implemented. Some background of the language and how to program in C-Lesh can be found here:

      http://www.codeloader.net/readme.html
      As I program this thing I will post code from different components and explain.
    • By isu diss
      I'm trying to duplicate vertices using std::map to be used in a vertex buffer. I don't get the correct index buffer(myInds) or vertex buffer(myVerts). I can get the index array from FBX but it differs from what I get in the following std::map code. Any help is much appreciated.
      struct FBXVTX { XMFLOAT3 Position; XMFLOAT2 TextureCoord; XMFLOAT3 Normal; }; std::map< FBXVTX, int > myVertsMap; std::vector<FBXVTX> myVerts; std::vector<int> myInds; HRESULT FBXLoader::Open(HWND hWnd, char* Filename, bool UsePositionOnly) { HRESULT hr = S_OK; if (FBXM) { FBXIOS = FbxIOSettings::Create(FBXM, IOSROOT); FBXM->SetIOSettings(FBXIOS); FBXI = FbxImporter::Create(FBXM, ""); if (!(FBXI->Initialize(Filename, -1, FBXIOS))) { hr = E_FAIL; MessageBox(hWnd, (wchar_t*)FBXI->GetStatus().GetErrorString(), TEXT("ALM"), MB_OK); } FBXS = FbxScene::Create(FBXM, "REALMS"); if (!FBXS) { hr = E_FAIL; MessageBox(hWnd, TEXT("Failed to create the scene"), TEXT("ALM"), MB_OK); } if (!(FBXI->Import(FBXS))) { hr = E_FAIL; MessageBox(hWnd, TEXT("Failed to import fbx file content into the scene"), TEXT("ALM"), MB_OK); } FbxAxisSystem OurAxisSystem = FbxAxisSystem::DirectX; FbxAxisSystem SceneAxisSystem = FBXS->GetGlobalSettings().GetAxisSystem(); if(SceneAxisSystem != OurAxisSystem) { FbxAxisSystem::DirectX.ConvertScene(FBXS); } FbxSystemUnit SceneSystemUnit = FBXS->GetGlobalSettings().GetSystemUnit(); if( SceneSystemUnit.GetScaleFactor() != 1.0 ) { FbxSystemUnit::cm.ConvertScene( FBXS ); } if (FBXI) FBXI->Destroy(); FbxNode* MainNode = FBXS->GetRootNode(); int NumKids = MainNode->GetChildCount(); FbxNode* ChildNode = NULL; for (int i=0; i<NumKids; i++) { ChildNode = MainNode->GetChild(i); FbxNodeAttribute* NodeAttribute = ChildNode->GetNodeAttribute(); if (NodeAttribute->GetAttributeType() == FbxNodeAttribute::eMesh) { FbxMesh* Mesh = ChildNode->GetMesh(); if (UsePositionOnly) { NumVertices = Mesh->GetControlPointsCount();//number of vertices MyV = new XMFLOAT3[NumVertices]; for (DWORD j = 0; j < NumVertices; j++) { FbxVector4 Vertex = Mesh->GetControlPointAt(j);//Gets the control point at the specified index. MyV[j] = XMFLOAT3((float)Vertex.mData[0], (float)Vertex.mData[1], (float)Vertex.mData[2]); } NumIndices = Mesh->GetPolygonVertexCount();//number of indices MyI = (DWORD*)Mesh->GetPolygonVertices();//index array } else { FbxLayerElementArrayTemplate<FbxVector2>* uvVertices = NULL; Mesh->GetTextureUV(&uvVertices); int idx = 0; for (int i = 0; i < Mesh->GetPolygonCount(); i++)//polygon(=mostly triangle) count { for (int j = 0; j < Mesh->GetPolygonSize(i); j++)//retrieves number of vertices in a polygon { FBXVTX myVert; int p_index = 3*i+j; int t_index = Mesh->GetTextureUVIndex(i, j); FbxVector4 Vertex = Mesh->GetControlPointAt(p_index);//Gets the control point at the specified index. myVert.Position = XMFLOAT3((float)Vertex.mData[0], (float)Vertex.mData[1], (float)Vertex.mData[2]); FbxVector4 Normal; Mesh->GetPolygonVertexNormal(i, j, Normal); myVert.Normal = XMFLOAT3((float)Normal.mData[0], (float)Normal.mData[1], (float)Normal.mData[2]); FbxVector2 uv = uvVertices->GetAt(t_index); myVert.TextureCoord = XMFLOAT2((float)uv.mData[0], (float)uv.mData[1]); if ( myVertsMap.find( myVert ) != myVertsMap.end() ) myInds.push_back( myVertsMap[ myVert ]); else { myVertsMap.insert( std::pair<FBXVTX, int> (myVert, idx ) ); myVerts.push_back(myVert); myInds.push_back(idx); idx++; } } } } } } } else { hr = E_FAIL; MessageBox(hWnd, TEXT("Failed to create the FBX Manager"), TEXT("ALM"), MB_OK); } return hr; } bool operator < ( const FBXVTX &lValue, const FBXVTX &rValue) { if (lValue.Position.x != rValue.Position.x) return(lValue.Position.x < rValue.Position.x); if (lValue.Position.y != rValue.Position.y) return(lValue.Position.y < rValue.Position.y); if (lValue.Position.z != rValue.Position.z) return(lValue.Position.z < rValue.Position.z); if (lValue.TextureCoord.x != rValue.TextureCoord.x) return(lValue.TextureCoord.x < rValue.TextureCoord.x); if (lValue.TextureCoord.y != rValue.TextureCoord.y) return(lValue.TextureCoord.y < rValue.TextureCoord.y); if (lValue.Normal.x != rValue.Normal.x) return(lValue.Normal.x < rValue.Normal.x); if (lValue.Normal.y != rValue.Normal.y) return(lValue.Normal.y < rValue.Normal.y); return(lValue.Normal.z < rValue.Normal.z); }  
  • Advertisement