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

Started by
21 comments, last by coope 8 years, 6 months ago

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..

Advertisement
struct MyStruct
{
   std::function<void ()> func;
   MyStruct() = default;
   MyStruct(const std::function<void ()>& func):func(func) { }
};

// ...

std::vector<MyStruct> funcs;
funcs.push_back(&myBla);
funcs.push_back(&myBlub);
for (const auto& f : funcs)
   f.func();
Obviously in this simple case you would rather store the std::function<> directly inside the container instead of putting them into a pointless structure.

Edit: Stupid forum editor mangled all template parameters...

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.

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.

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

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

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

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.

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

This topic is closed to new replies.

Advertisement