Sign in to follow this  
WiredCat

Assign different functions to different structs in std::vector<>

Recommended Posts

I am wondering if theres another way of executing a function like that:

keep in mind that this is pseudocode

 

struct MyStruct
{

void onClick(); //function that will be executed

};

 

std::vector<MyStruct> ahue;

 

 

void Button4_OnClick()

{

}

 

void main()

{

ahue[4].onClick() = Button4_OnClick(); 

}

 

do i have to always assign a pointer to function or theres another way, i am asking because in borland c++ builder [im not sure how they do that]

you place a button and theres already a code that executes onClick property. so you dont need to worry about setting pointers to a function etc..

 

 

Share this post


Link to post
Share on other sites

welp, thats not exactly what i expected.

But actually a constructor that assigns a function is quite an awesome trick to do :) +1

 

im waiting for more other examples to see what kind of thing would fit the most so far your wins.

 

I'm now implementing popup menus in opengl and i need to call a function (on item click) defined somewhere else in code.

Share this post


Link to post
Share on other sites
Basically, you're doing a call back function. BitMaster gave you the C++ method. Here is how it was done in C:
 
#include <iostream>

struct my_struct {
	void (*my_func)(); // The syntax here is important! You are not declaring a function. You're declaring a pointer to a function.
};

void test_func()
{
	std::cout << "test function" << std::endl;
}

int main()
{
	my_struct	test;

	test.my_func = &test_func; // Assign the address of test_func to test.my_func.

	test.my_func();

	while (1);
}
Tested and it works. I would say that BitMaster's is better as it does it in a C++ fashion, but I don't think it really matters.

You do have to assign the pointer to the function for this to work. "void" is a valid return type. This will not work:
 
test.my_func = test_func(); // The return value of test_func is being assigned to test.my_func instead of the address of test_func.
Edited by MarkS

Share this post


Link to post
Share on other sites

as stated above, procedural variables are the standard way to do it:

 

from caveman v3.0....

 

typedef:

typedef void functiontype1();
 
forward declaration:
void drinkliquid();
 
variable declaration:
struct actionrec
{
...
functiontype1 *proceedure;
...
};
 
 
assignment:
action[DRINK].proceedure=drinkliquid;
 
invocation:
void doactionmodeB(int cm1)
{
int a;
a=7+cm[cm1].mood/13;
if (frame<=a) action[cm[cm1].current_action].proceedure();
}
 

where cm[cm1].currect_action==DRINK

 

so doactionmodeB calls action[DRINK].porceedure(), which is void drinkliquid();

Share this post


Link to post
Share on other sites
Another way to do this is to use polymorphism. This isn't necessarily superior to the above, but I thought I'd mention it for completeness. It is used in some libraries.

The code equivalent to the example is: 
struct MyStruct
{
  virtual void onClick() = 0; //function that will be executed
};
 
std::vector<unique_ptr<MyStruct>> ahue;
 
 
void Button4_OnClick()
{
}
 
struct Button4: public MyStruct{
  void onClick()
  {
     Button4_OnClick()
  }
};

void main()
{  
  ahue.emplace_back(new Button4()); 
}

Share this post


Link to post
Share on other sites

Another way to do this is to use polymorphism. This isn't necessarily superior to the above, but I thought I'd mention it for completeness. It is used in some libraries.

The code equivalent to the example is: 

struct MyStruct
{
  virtual void onClick() = 0; //function that will be executed
};


Two nitpicks with that: first, anything with a virtual function really should get a virtual destructor as well. I once ran into really annoying leaks because someone else did not stick to that rule. Thankfully, many modern compilers will warn you about it.

Second, that's an implementation of the observer pattern. While it can be useful if you need more than one function to be called in response to different types of events but for just a single function I find it more like an anti-pattern imported from languages such as Java where you have no other choice.

Share this post


Link to post
Share on other sites

Another way to do this is to use polymorphism. This isn't necessarily superior to the above, but I thought I'd mention it for completeness. It is used in some libraries.

The code equivalent to the example is: 

struct MyStruct
{
  virtual void onClick() = 0; //function that will be executed
};


Two nitpicks with that: first, anything with a virtual function really should get a virtual destructor as well. I once ran into really annoying leaks because someone else did not stick to that rule. Thankfully, many modern compilers will warn you about it.

Second, that's an implementation of the observer pattern. While it can be useful if you need more than one function to be called in response to different types of events but for just a single function I find it more like an anti-pattern imported from languages such as Java where you have no other choice.

Both valid points. However, if you need one callback, you may soon need multiple. In this case you might want to support on_hover, on_select, and on_pressed (if it's possible to use the keyboard to select and press the button), so the observer pattern may be viable.

Share this post


Link to post
Share on other sites
True, however in a lot of cases it's not immediately obvious that you will ever need more than one callback type. In these cases I would strongly recommend against the polymorphism-way in C++.

In Java for example creating a quick anonymous inner class is extremely simple. It even has access to data from its enclosing scope without any extra work.
In C++ you need to define your (possibly local) observer implementation. If you need access to any data from an enclosing scope you have to transfer it there somehow which is some extra boilerplate. You might even be in a bit of a pickle regarding lifetime management of the observer.
Especially with C++11 these kind of problems don't really exist with lambdas. But even before C++11 you could reduce the complexity a lot with, for example, Boost's function objects, bind and lambdas.

Share this post


Link to post
Share on other sites
Description Resource Path Location Type
ISO C++ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to member function.  Say '&TTERRA_TEX_OSD::SetNoBlend' [-fpermissive]
 
 
 
code:
 
void (*MenuOnClick)();


void SetNoBlend() { brush_blend = 0; }


BrushMenu.Items[3].Items[0].MenuOnClick = &SetNoBlend;

Share this post


Link to post
Share on other sites

With a little help by stackoverflow: http://stackoverflow.com/questions/1485983/calling-c-class-methods-via-a-function-pointer

class Foo {
public:
    void f() { std::cout << "hello world" << std::endl; }
};

int main()
{
    Foo x;
    void (Foo::*g)();

    g = &Foo::f;
    (x.*g)();
    return 0;
}

Share this post


Link to post
Share on other sites

The problem is that a member function has an implicit "this" pointer as the first argument, to know what object to work with.

That's why assigning it to a void function pointer won't work.

Also, you need to keep the pointer to the object around, so you can pass it along when calling the callback later.

 

The C++11 way to solve it:

 

class Menu {

 std::function<void()> onMenuClick;

};

 

class Foo {

 void f();

}; 

 

 

Menu menu;

Foo x;

 

menu.onMenuClick = [&x] () { x.f() };  //using wrapper lambda

 

OR

 

menu.onMenuClick = std::bind(&Foo:f, &x); //using bind

Share this post


Link to post
Share on other sites

the thing is i store a pointer to function in one struct

 

 

then in another struct i try to assign the function (from this structure)

struct TDropDownMenu
{
pt2Function MenuOnClick;
TDropDownMenu * Items;
};


struct TTERRA_TEX_OSD
{
void SetNoBlend()
{
some code here
}


TDropDownMenu menu;
void Init()
{
menu.Items[0].Items[0].MenuOnClick = &SetNoBlend();
}

Thats what i am trying to do.

 

 

 

 

 

 

EDIT.

 

i did what 

Olof Hedman told to do.

after enabling c++11 support on android compiler it compiles, theres also lotta work to see if this works

 

but for now i thank you for support

 

 

now my code looks like this:

#include <functional>
struct TDropDownMenu
{

	 std::function<void()> MenuOnClick;
};



other header

struct TTERRA_TEX_OSD
{
void SetNoBlend() { brush_blend = 0; }
void Init()
{
BrushMenu.Items[3].Items[0].MenuOnClick = std::bind(&TTERRA_TEX_OSD::SetNoBlend, this);
}
};
Edited by WiredCat

Share this post


Link to post
Share on other sites

I encountered a problem with:

 

std::function<void(int)> SpecialMenuOnClick;

 

LayerMenu.Items[3+i].Items[0].SpecialMenuOnClick = std::bind(&TTERRA_TEX_OSD::SelectLayer, this);

 

void SelectLayer(int index)

{
selected_layer = index;
}
 
 
this doesn't work :/
 
 
using std::bind gives me an error:
 
 
no match for 'operator=' (operand types are 'std::function<void(int)>' and 'std::_Bind_helper<false, void (TTERRA_TEX_OSD::*)(int), TTERRA_TEX_OSD* const>::type {aka std::_Bind<std::_Mem_fn<void (TTERRA_TEX_OSD::*)(int)>(TTERRA_TEX_OSD*)>}')
 
Edited by WiredCat

Share this post


Link to post
Share on other sites
LayerMenu.Items[3+i].Items[0].SpecialMenuOnClick = std::bind(&TTERRA_TEX_OSD::SelectLayer, this, std::placeholders::_1);
Should probably do the trick although I have not used any bind since C++11 was around. See this for more details. Edited by BitMaster

Share this post


Link to post
Share on other sites


using std::bind gives me an error:
I would suggest against using std::bind, it has some caveats and it keeps getting worse and worse the more advanced usage you need for it. It is also particularly opaque to the compiler. It seems I'm not the only one. I mean seriously.

Just use a standard lambda. Is this what you're trying to do?

LayerMenu.Items[3+i].Items[0].SpecialMenuOnClick = [this]() { this->SelectLayer(); };

Share this post


Link to post
Share on other sites

Thanks for those video links Krohm.

 

I thought about adding something about preferring lambdas, but I didn't have time to write it clearly... (but at least I wrote the lambda as the first option in my post)

Didn't know bind was _that_ bad though, now I have more arguments for always using wrapper lambdas.

Share this post


Link to post
Share on other sites
LayerMenu.Items[3+i].Items[0].SpecialMenuOnClick = std::bind(&TTERRA_TEX_OSD::SelectLayer, this, std::placeholders::_1);

 compiles

 

 

but

LayerMenu.Items[3+i].Items[0].SpecialMenuOnClick = [this]() { this->SelectLayer(); };

doesn't

 

no match for 'operator=' (operand types are 'std::function<void(int)>' and 'TTERRA_TEX_OSD::ReInit_LayerMenu()::__lambda0')
 

 

can someone tell me why?

Share this post


Link to post
Share on other sites
The same reason as why you needed the _1 for the bind. You create a lambda taking no parameter and assign it to something expecting an int-argument.

LayerMenu.Items[3+i].Items[0].SpecialMenuOnClick = [this](int index) { this->SelectLayer(index); };

Share this post


Link to post
Share on other sites

I resorted to making a custom class for C++ callbacks.

 

The reason was that I felt std::function was a sort of 'god class' that can hold all the functions, including capturing lambda state. Except I didnt think I would ever need to capture lambda state. So all the std::function logic for doing heap allocations and probably some if branches here and there seemed like useless overhead to me.

 

What I wanted was simple: store a pointer to an object, and a function pointer to a member function. Leave object pointer nullptr if its a free function.

(for lambdas I take the pointer of the operator() of the lambda object - I dont capture the state, the user must keep ownership of the lambda state and ensure it doesnt disappear, like with any other C++ object)

 

Didnt turn out to be so simple.

 

I eventually got it working by creating some template stuff to 'wrap'/convert a member function pointer into a regular free function (with a 'this' pointer parameter). I didnt test this with virtual functions, but it compiles fine and works with regular, member, and existing lambdas.

 

(Its split to 3 classes to separately handle a return type vs no return type)

namespace wle
{
	namespace data
	{
		//Supports nonmember and member functions, lambdas (captured parameters are not stored!)
		template<typename ReturnT, typename... ParamT>
		class CallbackBase
		{
			//Member func as free function
			using MFuncT = ReturnT(*)(void*, ParamT...);
			//free function
			using FuncT = ReturnT(*)(ParamT...);
		public:
			CallbackBase()
			{ 
				reset();
			}
			template<typename ClassT, ReturnT(ClassT::*memberFunc)(ParamT...)>
			void connectMemberFunction(ClassT* object)
			{
				mObject = object;
				mMemberFunction = &memberFunctionPtrAsFreeFunction < ClassT, decltype(memberFunc), memberFunc, ParamT... > ;
			}
			template<typename LambdaT>
			//NOTE: Callback lifetime must not exceed that of lambda if variables are captured! Use connectFunction if lambda is noncapturing
			void connectLambda(LambdaT& lambda)
			{
				mObject = &lambda;
				mMemberFunction = &memberFunctionPtrAsFreeFunction < LambdaT, decltype(&LambdaT::operator()), &LambdaT::operator(), ParamT... > ;
			}
			void connectFunction(ReturnT(*func)(ParamT...))
			{
				mObject = nullptr;
				mFunction = func;
			}
			void reset()
			{
				mObject = nullptr;
				mFunction = nullptr;
			}
			//false for normal function, true for member function / lambda
			bool hasContext() const
			{
				return mObject != nullptr;
			}
			bool connected() const
			{
				return mFunction != nullptr;
			}
		protected:
			template<typename ClassT, typename MemberFuncT, MemberFuncT memberFunc, typename... SuppliedParamT>
			static ReturnT memberFunctionPtrAsFreeFunction(void* object, SuppliedParamT... params)
			{
				return (static_cast<ClassT*>(object)->*memberFunc)(params...);
			}
			void* mObject = nullptr;
			union
			{
				MFuncT mMemberFunction;
				FuncT mFunction;
			};
		};








		template<typename>
		class Callback;
		template<typename... ParamT>
		//Supports nonmember and member functions, lambdas (captured parameters are not stored!) [void return type]
		class Callback<void(ParamT...)> : public CallbackBase < void, ParamT... >
		{
			using CallbackT = Callback<void(ParamT...)>;
		public:
			void connectToStub()
			{
				connectFunction(&stub);
			}
			template<typename... SuppliedParamT>
			void operator()(SuppliedParamT&&... params)
			{
				assert(mFunction != nullptr);
				//Member function or lambda
				if (mObject != nullptr)
				{
					(*mMemberFunction)(mObject, std::forward<SuppliedParamT>(params)...);
				}
				//Nonmember function
				else
				{
					(*mFunction)(std::forward<SuppliedParamT>(params)...);
				}
			}
			explicit operator bool() const { return connected(); }
			bool operator==(const CallbackT& other) const { return mFunction == other.mFunction && mObject == other.mObject; }
			bool operator!=(const CallbackT& other) const { return !((*this) != other); }
		private:
			static void stub(ParamT...)
			{
			}
		};








		template<typename ReturnT, typename... ParamT>
		//Supports nonmember and member functions, lambdas (captured parameters are not stored!) [nonvoid return type]
		class Callback<ReturnT(ParamT...)> : public CallbackBase < ReturnT, ParamT... >
		{
			using CallbackT = Callback<ReturnT(ParamT...)>;
		public:
			template<ReturnT defaultValue = ReturnT()>
			void connectToStub()
			{
				connectFunction(&stub<defaultValue>);
			}
			template<typename... SuppliedParamT>
			ReturnT operator()(SuppliedParamT&&... params)
			{
				assert(mFunction != nullptr);
				//Member function or lambda
				if (mObject != nullptr)
				{
					return std::move((*mMemberFunction)(mObject, std::forward<SuppliedParamT>(params)...));
				}
				//Nonmember function
				else
				{
					return std::move((*mFunction)(std::forward<SuppliedParamT>(params)...));
				}
			}
			explicit operator bool() const { return connected(); }
			bool operator==(const CallbackT& other) const { return mFunction == other.mFunction && mObject == other.mObject; }
			bool operator!=(const CallbackT& other) const { return !((*this) == other); }
		private:
			template<ReturnT defaultValue>
			static ReturnT stub(ParamT...)
			{
				return defaultValue;
			}
		};
	}
}

Share this post


Link to post
Share on other sites

I'd tend to agree with the very first reply suggesting to use std::function<>. This avoids boxing you in if you decide the function calls should be defined as methods of a class instead. Function pointers are pretty simple to do though so they are worth some consideration as to whether you'll want to move things around or provide additional types of calls that can be made.

 

Anything beyond the following seems like you'd be taking on learning complicated syntax for the same semantics. Although I did see a suggestion about Lambdas which would be used with the function pointers and would be able to call object methods. A wrapper function for a method in other words.

#include <functional>
 
struct A
{
 std::function<void(arguments)> func;
}
 
or
 
struct B
{
 void (*func)(arguments);
}

 

std::function is simple and easily understood, so I'd use it.

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