I come from the C# world so events and event handlers are everyday stuff.
Recently i have been studying about wxWidgets
I've been googling about this for a week now and i have found that most C++ programmers hardly understand the term "event" or "callback", perhaps it's ambiguous in the world of C++.
Here's a simple example of the event model.
class A{
public:
A(){
child.MySuperClickEvent = this.HandleSuperClick;
}
private:
B child;
void HandleSuperClick(B child){
// do stuff
}
}
class B{
public:
/*TBA*/ MySuperClickEvent;
private:
void ClickPrivate(){
MySuperClickEvent(this);
}
}
class B has it's own designated purpose and when finished it triggers an event.
The idea is that class B shouldn't have any knowledge of it's domain, making it more reusable.
I have read about std::function<> and function pointers. which all seems to take examples in handling static functions but as soon as were talking "Member to Member" things starts to get greasy.
So summarize, the question is simple. How do i make a member function pointer that can be assigned to a member externally?
There are a great many ways to implement such things, varying in terms of memory overheads, how many callbacks are supported, thread safety, use of e.g. weak_ptrs to see if the objects involved still exist etc..
Just as a taste to get you started - here's a simple single-threaded "observer" implementation doing basically what you ask for in the question:
#include <iostream>
#include <list>
struct Observer
{
virtual ~Observer() { }
virtual void on_event_x() { }
};
class Observable
{
public:
void add_observer(Observer& o)
{
observers_.push_back(&o);
}
void do_some_stuff()
{
std::cout << "before 1st event\n";
fire_event_x();
std::cout << "between 1st & 2nd events\n";
fire_event_x();
std::cout << "after 2nd event\n";
}
private:
std::list<Observer*> observers_;
void fire_event_x()
{
for (auto& observer : observers_)
observer->on_event_x();
}
};
struct My_Observer : Observer
{
My_Observer(int id) : id_(id) { }
void on_event_x() override
{
std::cout << "My_Observer::on_event_x() id " << id_ << '\n';
}
int id_;
};
int main()
{
My_Observer my_observer_1 { 1 };
My_Observer my_observer_2 { 2 };
Observable x;
x.add_observer(my_observer_1);
x.add_observer(my_observer_2);
x.do_some_stuff();
}
Runtime output:
before 1st event
My_Observer::on_event_x() id 1
My_Observer::on_event_x() id 2
between 1st & 2nd events
My_Observer::on_event_x() id 1
My_Observer::on_event_x() id 2
after 2nd event
If that doesn't suit you functionally, please say exactly why.
As this question is tagged wxWidgets, let me answer the more narrow variant of it, i.e. how to use member function as event handler in wxWidgets:
This is done with the help of Bind() method which can be used with a member function directly (you pass the pointer to the function itself and the object to call it on) or any arbitrary functor, i.e. anything that can be called using the standard function call syntax, such as an std::function<> object which, in turn, can be used to store any callable object.
Related
So i have made this task system, or i'm trying to make it, so that if someone has done more than seven tasks, the next person has to do seven.
the doubles (Bodine, Finn, Tycho) are the persons. Now i dont know if i used it correctly,(if they need to be doubles) and how i use them in this line of code:
if (taskNumbers == 7)
{
std::cout << "It's " + /*what to do here? */ + "time!";
what i want is that if the task numbers are higher than seven, and Bodine has done 7 tasks, it says "Its finn his time!"
#include <iostream>
using namespace std;
double Bodine;
double Finn;
double Tycho;
bool tasksDone = true;
int taskNumbers = 0;
int main()
{
if (taskNumbers > 7)
{
std::cout << "It's " + /*what to do here? */ + "time!";
return 1;
}
}
This is one of my first projects yet(im 13, so yeah....). I only need to know how to use the next variable!
Well, you're not really keeping track of whose turn it is. Also, double is not the right tool for the job here. I would do something like this:
std::vector<std::string> users = { "Bodine", "Finn", "Tycho" };
int currentUser = 0;
Now we have a list of names as well as a counter that says whose turn it is. 0 is for the first person, 1 for the second person, and 2 for the third person. Then we need the logic to advance that counter:
void advanceUser() {
if (++currentUser >= users.size()) {
currentUser = 0;
}
}
This increases currentUser, and when it is bigger than the amount of people, it loops back to 0, so the people take turns correctly.
Next about the task logic, I would suggest something like this:
int tasksDone = 0;
void incrementTasksDone() {
if (++tasksDone >= 7) {
advanceUser();
std::cout << "It's " + users[currentUser] +"'s time!" << std::endl;
tasksDone = 0;
}
}
Here we have a counter that keeps track of the amount of tasks, and when it hits 7, it sets it back to 0 and announces that it's the next user's turn after callling advanceUser to set that right.
You can then call incrementTasksDone however you want, for instance I tested it like this:
int main()
{
for (int i = 0; i < 100; i++) {
incrementTasksDone();
}
}
That would get 100 tasks done, for instance, so the turns would change 14 times in the process and it would print the message every time.
Also, to get this example to run, be sure to add the following includes:
#include <vector>
#include <string>
#include <iostream>
Hello and welcome to C++. Many will say that C++ is a bad language of choice for starters. Well I started with C++ myself a long time ago and I'm still learning to this day. I am 100% self taught and I didn't have the advantages that people do today with the amount of information that is available over the internet as well as the modern formats that are available. When I first started learning C++, the internet was in the stage of booming; but most websites back then were nearly pure text and even simple pictures or graphics took a while to load to the screen because this was in the era of Dial Up. Today, people who are starting out have the advantages of both this website, other similar sites and even youtube videos. However, I still enjoy helping where I can because it doesn't only help you, but it also helps me to improve what I have already learned. C++ has evolved over the years so what I'll do here is demonstrate to you a small application that I believe mimics the behavior of what you have described you are trying to do. Some of these techniques are a little advanced especially for beginners, however I think it is a good fit that someone who is new learns these concepts early.
Storage Types & Lifetime - There are basically 4 main storage types in C++: Automatic, Dynamic, Static and Thread. I mostly focus on the first 3.
Automatic: It has the lifetime of the scope that it is declared in and will automatically be destroyed once that scope exits from its closing brace }
Dynamic: Memory that is represented by pointers but declared with new and must have a matching delete, or arrays new[] and delete[] respectively. They can live longer than the scope they are declared in. They will live until their matching delete is called. If no matching delete takes place, this leads to a memory leak, invalid pointers, dangling pointers & references and undefined behavior. Special Care needs to be taken when using raw-pointers; it's advisable to use either containers or smart pointers.
Static: These are typically found in the global namespace and or global filespace. If a static is declared in the main.cpp it will have a lifetime of the application and the scope of the whole program. If they are declared in other cpp files, they will have the scope of that file, unless if they are declared in some header file, then they will have the scope of what other translation unit includes that header. They are similar to Automatic in the sense they will automatically be destroyed, but they differ as in the fact that they are initialized only once, the maintain their state and you can only have a single instance of them.
For a demonstration of the different types of storage classifications you can see my previous answer to this Q/A.
Classes and Inheritance: - (I will not involve Polymorphism).
Classes:
Classes and Structs are user defined data types.
The difference between the two is the default access
By default: Structs have Public Members & Classes have Private Members
Members of a Class or Struct can be any built in type, pointers to types, another user defined data type, and methods or functions.
Member Variables can be of any Storage Type: Automatic, Dynamic, Static and Thread, however, member functions are usually Automatic but can be Static. If you have a member variable it has to be initialized outside of the Class's Declaration to resolve it's symbols.
They have Constructors and Destructors by default, but one can create their own custom Constructor or Destructor. You will typically see people mention them by their short names: ctor & dtor respectively.
One class can inherit from another.
Inheritance:
You have Base or Super Classes, and you have Derived or Child Classes
When Inheriting from a Base class, if the Base class's ctor is Public, this means you can create an object of both the Base & Derived Classes. Sometimes you want this kind of design, but there are times when you don't.
If you do not want the user to be able to create an instance of the Base Class, but are able to create an instance of the Derived Class then all you need to do is make sure that 1st its ctor is Protected. If you declare it Private, then even your Derived Classes can not access it and therefore they can not be declared. *2nd You want to make sure that your Destructors - dtors are Virtual; otherwise you will have issues with the order in which classes are destroyed.
A Base Class can have Member Functions that are Virtual which means All Derived Classes must Implement that Function.
A Base Class can have Member Functions that are Purely Virtual which is similar above, but also prevents anyone from declaring an instance of this Base Class because this makes the Base Class Abstract which means it's an idea for an interface.
Example Virtual Function: virtual update();
Example Purely Virtual : `virtual update() = 0;
Containers - (I will use a std::container, but I won't involve any Algorithms)
There are many kinds of containers for different needs; these containers help to keep your program simple, easy to manage and debug, user friendly and to prevent many future headaches instead of using basic C Arrays.
There are a few different types and they each have their own purpose and properties which I will not go over all of their details as you can find a plethora of information on the net regarding them, but I will label and group them to their similar properties: The groupings are Sequence Containers, Associative Containers, & Unordered Containers and these all belong to the std:: namespace. The major difference between the groupings are: Sequence Containers are like arrays and linked lists, Associative Containers are binary trees, and Unordered Containers are similar to the binary trees except they are not in order, they are considered hash tables.
Sequence: vector, dequeue, queue, list, forward_list, array
Associative: set, multiset, map, multimap
Unordered: unordered_set, unordered_multiset, unordered_map, unordered_multimap`
What makes these containers powerful is the ability to either traverse them quickly or to insert and find quickly depending on which container you are using. Another good feature is helping in the process of memory management. Finally is the numerous algorithms and iterators that can do work on them.
For more information on the stl I advise to watch this youtube series by Bo Qian
Smart Pointers There are a few different types, but the most important two are std::shared_ptr<T> and std::unique_ptr<T>. The major difference between the two is that shared has a reference count to how many objects have access to it; it means it has a public type interface and can use copy semantics; unique on the other hand has only 1 owner at a time, it can transfer ownership but once it does the original owner no longer has access to it. You can not copy unique, but you can move them. These smart pointers help with the use of dynamic memory and life time object management to prevent memory leaks, invalid pointers, dangling pointers and references, undefined behavior etc. They help to minimize the use of new & delete and new[] & delete[] respectively.
Now that you have an understanding of some of the concepts that you will see quite often in C++ programs; I will now show you the simple application that I have written based off of what it was that I perceived you were trying to do:
#include <array>
#include <exception>
#include <memory>
#include <string>
#include <iostream>
class Person {
protected:
std::string name_;
int tasksCompleted_;
public:
const std::string& whoIs() const { return name_; }
int numberTasksCompleted() const { return tasksCompleted_; }
virtual void performTask() = 0;
protected:
explicit Person(const std::string& name) : name_{ std::move(name) } {}
virtual ~Person() = default;
};
class Bodine final : public Person {
private:
static int currentTask;
public:
explicit Bodine(const std::string& name = std::string("Bodine")) : Person(name) {}
virtual ~Bodine() = default;
virtual void performTask() { currentTask++; tasksCompleted_ = currentTask; }
};
int Bodine::currentTask = 0;
class Finn final : public Person {
private:
static int currentTask;
public:
explicit Finn(const std::string& name = std::string("Finn")) : Person(name) {}
virtual ~Finn() = default;
virtual void performTask() { currentTask++; tasksCompleted_ = currentTask; }
};
int Finn::currentTask = 0;
class Tycho final : public Person {
private:
static int currentTask;
public:
explicit Tycho(const std::string& name = std::string("Tycho")) : Person(name) {}
virtual ~Tycho() = default;
virtual void performTask() { currentTask++; tasksCompleted_ = currentTask; }
};
int Tycho::currentTask = 0;
int main() {
try {
std::array<std::shared_ptr<Person>, 3> people{
std::shared_ptr<Person>(new Bodine()),
std::shared_ptr<Person>(new Finn()),
std::shared_ptr<Person>(new Tycho())
};
// For each person in array
const int MAX_TASKS = 7;
int currentPerson = 0;
for (auto& p : people) {
std::cout << p->whoIs() << " has performed task #:\n";
while (p->numberTasksCompleted() < 7) {
p->performTask();
std::cout << p->numberTasksCompleted() << '\n';
if (p->numberTasksCompleted() == MAX_TASKS) {
currentPerson++;
if (currentPerson <= (people.size() - 1) ) {
std::cout << "It's your turn " << people[currentPerson]->whoIs() << " to do some tasks.\n";
}
break;
}
}
}
} catch( std::runtime_error& e ) {
std::cerr << e.what() << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
-Output-
Bodine has performed task #:
1
2
3
4
5
6
7
Finn has performed task #:
1
2
3
4
5
6
7
Tycho has performed task #:
1
2
3
4
5
6
7
Link to above program.
I know this is quite a bit to read; but please look closely over the application to try and see what it is doing. You can copy and paste this right into your own IDE and try to build and run it to see it in action. Hopefully this will give you a grasp of what C++ is.
-Edit-
About the code above: it is a little more complex than what it truly needs to be. First off you would not really see a single person's identity as it's own class. I only did this to demonstrate what inheritance is. I choose to use std::array instead of std::vector mainly due to the fact that you had 3 specific people from your proposal. If you have an unknown amount of people at compile time then std::vector would be the correct container to use. You wouldn't really need to use inheritance either. Also if you look at the 3 derived classes there is a lot of duplicate code. I also used shared_ptrs to show how they can be used, but are not necessary in your specific situation. I also used static member variables in the Derived Classes to show static storage.
Here is a simplified version of the above code to mimic your behavior. Both programs do the same task similarly, the only major differences are: The use of inheritance with purely virtual methods, static member storage, container type, and dynamic memory via the use of shared_ptr.
#include <exception>
#include <iostream>
#include <string>
#include <vector>
class Person {
private:
std::string name_;
int tasksCompleted_;
public:
explicit Person(const std::string& name) : name_(name), tasksCompleted_(0) {}
const std::string& whoIs() const { return name_; }
int numberTasksCompleted() const { return tasksCompleted_; }
void performTask() { tasksCompleted_++; }
};
int main() {
try {
std::vector<Person> people{
Person( "Bodine" ),
Person( "Finn" ),
Person( "Tycho" )
};
// For each person in array
const int MAX_TASKS = 7; // Don't like magic numbers!
int currentPerson = 0; // Needed variable
for (auto& p : people) {
std::cout << p.whoIs() << " has performed task #:\n";
while (p.numberTasksCompleted() < MAX_TASKS) {
p.performTask();
std::cout << p.numberTasksCompleted() << '\n';
if (p.numberTasksCompleted() == MAX_TASKS) {
currentPerson++;
if (currentPerson <= (people.size() - 1) ) {
std::cout << "It's your turn " << people[currentPerson].whoIs() << " to do some tasks.\n";
}
break;
}
}
}
} catch( std::runtime_error& e ) {
std::cerr << e.what() << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
Here is the link for this version of the program!
So I started a small little project to work on while I am learning. Basically, what I'm trying to do is a small "game" which I plan to build on as I learn new things.
Here is a brief description and my problem.
Basically, I want to assign various Hero types to a player based on their choice.
I made a base class "Hero" with only a HP parameter so far. After that, I made 2 derived classes from Hero, HeroType1, HeroType2 which will have specific abilities and so on.
I decided on storing various hero types in std::vector<Hero*> Heroes. Basically, I start my "game" by calling initializeHeroes function which, depending on the player choice creates a new object of type NewHero1 or NewHero2 and stores it an the vector mentioned before. The thing is, no matter what I tried so far, I can't access derived member functions when I want to use them later, only those of the Hero class.
What feels like a good solution: declare global variables player1, player2 and assign to them after players choose the HeroType. However, I can't do that because the data type has to be known before compiling. Sorry if this is a stupid and basic question, my knowledge is still very limited and that is why I am asking for some hints here.
I'd kindly like to ask on how would you approach this, I know it is a very simple issue, but I'm still a beginner and I'm trying to figure out the best way to solve this. Thanks in advance.
If you would like to call a member function from a element from std::vector<Hero*> Heroes and you know somehow that this element points to a Hero2-type, then you could create a new temporary variable Hero2 * tmpPtr and set this variable to the element whose memberfunction you want to call (tmpPtr = Heroes[i]). Then you should be able to call a memberfunction like this: tmpPtr->hero2Memberfuncion().
Full code:
#include <iostream>
#include <vector>
class SomeClass
{
public:
void a() {
std::cout << "a" << std::endl;
}
};
class AnotherClass : public SomeClass
{
public:
void b() {
std::cout << "b" << std::endl;
}
};
void main() {
std::vector<SomeClass *> vec;
AnotherClass v;
vec.push_back(&v);
AnotherClass * tmpPtr = (AnotherClass *)vec[0];
tmpPtr->b(); //Output: "b"
}
However if you want for example loop through the whole vector and for every element run a memberfunction that has the same name but the body of that function differs depending on to what Hero-type the element points, then you may want to use virtual functions. Example:
#include <iostream>
#include <vector>
class SomeClass
{
public:
virtual void a() {
std::cout << "from SomeClass" << std::endl;
}
};
class AnotherClass : public SomeClass
{
public:
void a() {
std::cout << "from AnotherClass" << std::endl;
}
};
void main() {
std::vector<SomeClass *> vec;
AnotherClass v1;
vec.push_back(&v1);
vec[0]->a(); //Output: "from AnotherClass"
SomeClass v2;
vec.push_back(&v2);
vec[1]->a(); //Output: "from SomeClass"
}
I'm pretty new to C++ and am having trouble making a pointer point from one class to another. This is what I have, it compiles without error, but doesn't work the way I want it to.
JungleMap *Map;
class JungleMap
{
public:
void goNorth()
{
cout << "You are going north towards the river.\n";
delete[] Map;
RiverMap *Map;
}
}
class RiverMap
{
public:
void goNorth()
{
cout << "You are going north away from the river.\n";
delete[] Map;
JungleMap *Map;
}
}
int main()
{
Map->goNorth();
Map->goNorth();
}
This is what the output is:
You are going north towards the river.
You are going north towards the river.
And this is what I would like the output to be:
You are going north towards the river.
You are going north away from the river.
How do I achieve this? It's really bugging me, especially since it compiles without problems.
Just creating a JungleMap* doesn't create a JungleMap. You formed a pointer, but didn't point it anywhere!
This is particularly dangerous since you then dereference it, and later attempt to delete through it. Yes, this compiles, because a compiler cannot diagnose this in the general case (and is never required to try), but you'll get everything at runtime from silent nothingness, to a crash, to a nuclear explosion.
You are also trying to invoke different functions in two different classes, through changing the type of a pointer (without any inheritance, at that), which is simply not possible and will prevent your code from compiling, even though you've tried to get around it by redeclaring variables locally. I could list a ream of misunderstandings but suffice it to say it's time to read a good introductory C++ book.
I would suggest a combination of inheritance and dynamic allocation, if I knew what you were trying to achieve. A common mistake on SO is to provide nonsense code, then expect us to know what your goal is from that nonsense code; unfortunately we have about as much idea what you really meant to do as the C++ compiler does!
You could make this work (to at least a minimal degree) by creating a base class from which both JungleMap and RiverMap derive. You'd then have a pointer to the base class, which you'd point at an instance of one of the derived classes. You'll also need to rearrange the code somewhat to get it to compile.
class Map {
public:
virtual void goNorth() { cout<<"Sorry, you can't go that way"; }
virtual void goSouth() { cout<<"Sorry, you can't go that way"; }
};
Map *map;
class RiverMap;
class JungleMap : public Map {
public:
void goNorth();
};
class RiverMap : public Map {
public:
void goSouth();
};
void JungleMap::goNorth() {
cout<<"You are going north towards the river.\n";
delete map;
map=new RiverMap;
}
void RiverMap::goSouth() {
cout<<"You are going south towards the jungle.\n";
delete map;
map=new JungleMap;
}
Note: here I'm just trying to say as close to your original design as possible and still have some code that might at least sort of work. I'm certainly not holding it up as an exemplary design, or even close to it (because, frankly, it's not).
What you should do is to sit down and think about the problem you are trying to solve, and make a proper design. In your case you have two "locations", and the "player" should be able to move between these locations. Starting from that we have identified two possible classes (Location and Player) and one behavior (the player can move from location to location).
With the above information, you could do something like this:
class Location
{
public:
void setNorth(Location* loc)
{
north_ = loc;
}
Location* getNorth() const
{
return north_;
}
void setSouth(Location* loc)
{
south_ = loc;
}
Location* getSouth() const
{
return south_;
}
void setDescription(const std::string& descr)
{
description_ = descr;
}
const std::string& getDescription() const
{
return description_;
}
protected:
Location() {} // Made protected to prevent direct creation of Location instances
private:
Location* north_;
Location* south_;
std::string description_;
};
class Jungle : public Location
{
public:
Jungle() : Location()
{
setDescription("You are in a jungle.");
}
};
class River : public Location
{
public:
River() : Location()
{
setDescription("You are close to a river.");
}
};
// The actual "map"
std::vector<Location*> map
void createMap()
{
map.push_back(new Jungle);
map.push_back(new River);
map[0]->setNorth(map[1]);
map[1]->setSouth(map[0]);
}
class Player
{
public:
Player(Location* initialLocation)
: currentLocation_(initialLocation)
{
std::cout << currentLocation_->getDescription() << '\n';
}
...
// Other methods and members needed for a "player"
void goNorth()
{
if (currentLocation_ && currentLocation_->getNorth())
{
currentLocation_ = currentLocation_->getNorth();
std::cout << currentLocation_->getDescription() << '\n';
}
}
void goSouth()
{
if (currentLocation_ && currentLocation_->getSouth())
{
currentLocation_ = currentLocation_->getSouth();
std::cout << currentLocation_->getDescription() << '\n';
}
}
private:
Location* currentLocation_; // The players current location
};
int main()
{
createMap(); // Create the "map"
Player player(map[0]); // Create a player and place "him" in the jungle
// Move the player around a little
player.goNorth();
player.goSouth();
}
In the code above, you have a single player object, which have a "current location". When you move the player around, you simply change the current location for that player. The current location of the player acts as the global Map variable you have.
Note: I'm not saying that this is a good design or code, just that it's simple.
However, if you're truly new to C++, you should probably start with some simpler problems, including tutorials on pointers and inheritance.
You appear to be confusing declaration with assignment.
The following line of code is called a declaration, it tells the compiler the properties and attributes of a thing.
JungleMap *Map;
After this line of code, the compiler knows that "Map" is a symbol (a name) referring to a pointer to a JungleMap.
The compiler doesn't have to do anything with a declaration, unless it would have a side effect, at which point it becomes a definition, which means that the declaration invokes a non-trivial constructor or provides an assignment:
struct Foo {};
struct Baz { Baz() { std::cout << "Baz is here\n"; } };
These are declarations - they don't create instances of objects, they describe the layout and functions for instances. At some point you have to create a concrete instance of them with a definition or a call to new.
struct Foo {};
struct Bar { Bar() { std::cout << "Bar is here\n"; } };
struct Baz {};
int main() {
int i; // no side effects, i is trivial.
char* p; // no side effects, p is a pointer (trivial) type
std::string* sp; // trivial, pointer
Foo f; // trivial
Bar b; // non-trivial, baz has a user-defined ctor that has side-effects.
Bar* bar; // trivial, unassigned pointer type.
Bar* bar2 = new Bar(); // side effects.
Bar bar(); // syntax error, "the most vexing parse"
}
In the above code, we never use "Baz" and we never declare an object of type Baz so the compiler essentially throws it away. Because so many of the variables are trivial and have no side effect, the result of compiling the above will be functionally equivalent to if we had written:
struct Foo {};
struct Bar { Bar() { std::cout << "Bar is here\n"; } };
int main() {
Bar* bar2 = new Bar(); // side effects.
Bar bar(); // syntax error, "the most vexing parse"
}
All of the rest does nothing.
C++ also allows you to re-use names as long as they are in different scopes, but this creates a new, hidden ("shadow") thing:
#include <iostream>
int main() {
int i = 1;
if (i == 1) {
float i = 3.141;
std::cout << "inner i = " << i << '\n';
}
std::cout << "outer i = " << i << '\n';
return 0;
}
The code you wrote will therefore compile, because it is declaring a new and private "Map" inside each of the go functions and then simply never using them.
Note that above I was able to declare i differently inside the inner scope than the outer.
C++ does not allow you to change the type of a variable - in the above code there are two variables called i. When we created the second i, it is a second variable called i the original variable didn't change.
In order to do what you are trying to do, you're going to need to learn about "polymorphism" and "inheritance", C++ concepts that will allow you to describe a "Room" or "Location" and then base JungleMap and RiverMap on that base definition such that you can take a pointer to the core concept, the Room, and write generic code that deals with rooms while moving the specifics of Jungle, River or BridgeMap into specialized functions. But I think that's beyond the scope of a reply here.
I have a series of classes A, B, ... which have many derived classes which are created inside a module I do not wish to change.
Additionally, I have at least one class Z, which has to be informed whenever an object of type A (or derived classes) is created or destroyed. In the future, there may be more classes, Y, X that want to observe different objects.
I am looking for a convenient way to solve this.
At first glance, the problem seemed trivial, but I'm kind of stuck right now.
What I came up with, is two base classes SpawnObserver and SpawnObservable which are supposed to do the job, but I am very unhappy with them for several reasons (see attached simplification of these classes).
When Z is notified, the actual object is either not yet or not anymore existent, due to the order in which base classes are created/destroyed. Although the pointers can be compared when destroying an object (to remove them from some data-structures in Z) this does not work when it is created and it surely does not work when you have multiple inheritance.
If you want to observe only one class, say A, you are always notified of all (A, B, ...).
You have to explicitly if/else through all classes, so you have to know all classes that inherit from SpawnObservable, which is pretty bad.
Here are the classes, which I tried to trim down to the most basic functionality, which you need to know to understand my problem. In a nutshell: You simply inherit from SpawnObservable and the ctor/dtor does the job of notifying the observers (well, at least, this is what I want to have).
#include <list>
#include <iostream>
class SpawnObservable;
class SpawnObserver {
public:
virtual void ctord(SpawnObservable*) = 0;
virtual void dtord(SpawnObservable*) = 0;
};
class SpawnObservable {
public:
static std::list<SpawnObserver*> obs;
SpawnObservable() {
for (std::list<SpawnObserver*>::iterator it = obs.begin(), end = obs.end(); it != end; ++it) {
(*it)->ctord(this);
}
}
~SpawnObservable() {
for (std::list<SpawnObserver*>::iterator it = obs.begin(), end = obs.end(); it != end; ++it) {
(*it)->dtord(this);
}
}
virtual void foo() {} // XXX: very nasty dummy virtual function
};
std::list<SpawnObserver*> SpawnObservable::obs;
struct Dummy {
int i;
Dummy() : i(13) {}
};
class A : public SpawnObservable {
public:
Dummy d;
A() : SpawnObservable() {
d.i = 23;
}
A(int i) : SpawnObservable() {
d.i = i;
}
};
class B : public SpawnObservable {
public:
B() { std::cout << "making B" << std::endl;}
~B() { std::cout << "killing B" << std::endl;}
};
class PrintSO : public SpawnObserver { // <-- Z
void print(std::string prefix, SpawnObservable* so) {
if (dynamic_cast<A*>(so)) {
std::cout << prefix << so << " " << "A: " << (dynamic_cast<A*>(so))->d.i << std::endl;
} else if (dynamic_cast<B*>(so)) {
std::cout << prefix << so << " " << "B: " << std::endl;
} else {
std::cout << prefix << so << " " << "unknown" << std::endl;
}
}
virtual void ctord(SpawnObservable* so) {
print(std::string("[ctord] "),so);
}
virtual void dtord(SpawnObservable* so) {
print(std::string("[dtord] "),so);
}
};
int main(int argc, char** argv) {
PrintSO pso;
A::obs.push_back(&pso);
B* pb;
{
std::cout << "entering scope 1" << std::endl;
A a(33);
A a2(34);
B b;
std::cout << "adresses: " << &a << ", " << &a2 << ", " << &b << std::endl;
std::cout << "leaving scope 1" << std::endl;
}
{
std::cout << "entering scope 1" << std::endl;
A a;
A a2(35);
std::cout << "adresses: " << &a << ", " << &a2 << std::endl;
std::cout << "leaving scope 1" << std::endl;
}
return 1;
}
The output is:
entering scope 1
[ctord] 0x7fff1113c640 unknown
[ctord] 0x7fff1113c650 unknown
[ctord] 0x7fff1113c660 unknown
making B
adresses: 0x7fff1113c640, 0x7fff1113c650, 0x7fff1113c660
leaving scope 1
killing B
[dtord] 0x7fff1113c660 unknown
[dtord] 0x7fff1113c650 unknown
[dtord] 0x7fff1113c640 unknown
entering scope 1
[ctord] 0x7fff1113c650 unknown
[ctord] 0x7fff1113c640 unknown
adresses: 0x7fff1113c650, 0x7fff1113c640
leaving scope 1
[dtord] 0x7fff1113c640 unknown
[dtord] 0x7fff1113c650 unknown
I want to stress, that I am perfectly aware why my solution behaves the way it does. My question is whether you have a better approach of doing this.
EDIT
As an extension to this question (and inspired by the comments below), I'd like to know:
Why do you think this is a terrible approach?
As an additional note: What I an trying to accomplish by this is to install a normal Observer in each and every created object.
EDIT 2
I will accept an answer that solves problem 1 (bold one in the enumeration above) or describes why the whole thing is a very bad idea.
Use the curiously recurring template pattern.
template<typename T> class watcher {
typename std::list<T>::iterator it;
watcher();
~watcher();
void ctord(T*);
void dtord(T*);
};
template<typename T> class Observer {
public:
typedef std::list<T*> ptr_list;
static ptr_list ptrlist;
typedef typename ptr_list::iterator it_type;
it_type it;
typedef std::list<watcher<T>*> watcher_list;
static watcher_list watcherlist;
typedef typename watcher_list::iterator watcher_it_type;
Observer() {
ptrlist.push_back(this);
it_type end = ptrlist.end();
end--;
it = end;
for(watcher_it_type w_it = watcherlist.begin(); w_it != watcherlist.end(); w_it++)
w_it->ctord(this);
}
~Observer() {
ptrlist.erase(it);
for(watcher_it_type w_it = watcherlist.begin(); w_it != watcherlist.end(); w_it++)
w_it->ctord(this);
}
};
class A : public Observer<A> {
};
class B : public Observer<B> {
};
class C : public A, public B, public Observer<C> {
// No virtual inheritance required - all the Observers are a different type.
};
template<typename T> watcher<T>::watcher<T>() {
Observer<T>::watcherlist.push_back(this);
it = watcherlist.end();
it--;
}
template<typename T> watcher<T>::~watcher<T>() {
Observer<T>::watcherlist.erase(it);
}
template<typename T> void watcher<T>::ctord(T* ptr) {
// ptr points to an instance of T that just got constructed
}
template<typename T> void watcher<T>::dtord(T* ptr) {
// ptr points to an instance of T that is just about to get destructed.
}
Not just that, but you can inherit from Observer multiple times using this technique, as two Observer<X> and Observer<Y> are different types and thus doesn't require diamond inheritance or anything like that. Plus, if you need different functionality for Observer<X> and Observer<Y>, you can specialize.
Edit # Comments:
class C DOES inherit from Observer<A> and Observer<B> through A and B, respectively. It doesn't need to know or care whether or not they're being observed. A C instance will end up on all three lists.
As for ctord and dtord, I don't actually see what function they perform. You can obtain a list of any specific type using Observer::ptrlist.
Edit again: Oooooh, I see. Excuse me a moment while I edit some more. Man, this is some of the most hideous code I've ever written. You should seriously consider not needing it. Why not just have the objects that need to be informed about the others do their creation?
Issue 1 isn't easily solved (in fact I think it's impossible to fix). The curiously recurring template idea comes closest to solving it, because the base class encodes the derived type, but you'll have to add a base to every derived class, if you really insist on knowing the derived type when the base is being constructed.
If you don't mind performing your actual operations (other than the bookkeeping, I mean) or examining the list outside the constructor or destructor of each object, you could have it (re)build the minimal list only when the operation is about to be performed. This gives you a chance to use the fully-constructed object, and makes it easier to solve issue 2.
You'd do this by first having a list of objects that have been constructed, but aren't on the 'full' list. And the 'full' list would contain two pointers per constructed object. One is the pointer to the base class, which you'll store from the Observable constructor, possibly multiple times during the construction of a single object. The other is a void *, pointing to the most derived part of the object -- use dynamic_cast<void *> to retrieve this -- and is used to make sure that each object only appears once in the list.
When an object is destroyed, if it has multiple Observable bases, each will try to remove itself from the lists, and when it comes to the full list, only one will succeed -- but that's fine, because each is equally good as an arbitrary base of that object.
Some code follows.
Your full list of objects, iterable in as straightforward a fashion as std::map will allow. (Each void * and each Observable * is unique, but this uses the Observable * as the key, so that it's easy to remove the entry in the Observable destructor.)
typedef std::map<Observable *, void *> AllObjects;
AllObjects allObjects;
And your list of objects that have been constructed, but aren't yet added to allObjects:
std::set<Observable *> recentlyConstructedObjects;
In the Observable constructor, add the new object to the list of pending objects:
recentlyConstructedObjects.insert(this);
In the Observable destructor, remove the object:
// 'this' may not be a valid key, if the object is in 'allObjects'.
recentlyConstructedObjects.erase(this);
// 'this' may not be a valid key, if the object is in 'recentlyConstructedObjects',
// or this object has another Observable base object and that one got used instead.
allObjects.erase(this);
Before you're about to do your thing, update allObjects, if there've been any objects constructed since last time it was updated:
if(!recentlyConstructedObjects.empty()) {
std::map<void *, Observable *> newObjects;
for(std::set<Observable *>::const_iterator it = recentlyConstructedObjects.begin(); it != recentlyConstructedObjects.end(); ++it)
allObjectsRev[dynamic_cast<void *>(*it)] = *it;
for(std::map<void *, Observable *>::const_iterator it = newObjects.begin(); it != newObjects.end(); ++it)
allObjects[it->second] = it->first;
recentlyConstructedObjects.clear();
}
And now you can visit each object the once:
for(std::map<Observable *,void *>::const_iterator it = allObjects.begin(); it != allObjects.end(); ++it) {
// you can dynamic_cast<whatever *>(it->first) to see what type of thing it is
//
// it->second is good as a key, uniquely identifying the object
}
Well... now that I've written all that, I'm not sure whether this solves your problem. It was interesting to consider nonetheless.
(This idea would solve one of the problems with the curiously recurring template, namely that you have lots of base objects per derived object and it's harder to disentangle because of that. (Unfortunately, no solution to the large number of base classes, sorry.) Due to the use of dynamic_cast, of course, it's not much use if you call it during an object's construction, which is of course the advantage of the curiously recurring thing: you know the derived type during the base's construction.
(So, if your'e going with that style of solution, AND you are OK with performing your operations outside the construction/destruction stage, AND you don't mind the (multiple) base classes taking up space, you could perhaps have each base's constructor store some class-specific info -- using typeid, perhaps, or traits -- and merge these together when you build the larger list. This should be straightforward, since you'll know which base objects correspond to the same derived object. Depending on what you're trying to do, this might help you with issue 3.)
Take a look at Signals and Slots especially Boost Signals and Slots
I wanted to be able to have something like Java's interface semantics with C++. At first, I had used boost::signal to callback explicitly registered member functions for a given event. This worked really well.
But then I decided that some pools of function callbacks were related and it made sense to abstract them and register for all of an instance's related callbacks at once. But what I learned was that the specific nature of boost::bind and/or taking the value of this seemed to make that break. Or perhaps it was just the fact that the add_listener(X &x) method declaration changed the code that boost::bind generated.
I have a very rough understanding why the problem occurred and I think it's probably functioning correctly as per its design. I am curious: what should I have done instead? Surely there's a Right Way to do it.
Here's some example code:
#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <iostream>
using namespace std;
struct X;
struct Callback
{
virtual void add_listener(X &x) = 0;
};
struct X
{
X() {}
X(Callback &c) { c.add_listener(*this); }
virtual void go() { cout << "\t'" << __PRETTY_FUNCTION__ << "'" << endl; }
};
struct CallbackReal : public Callback
{
virtual void add_listener(X &x)
{
f = boost::bind<void>(boost::mem_fn(&X::go), x);
}
void go() { f(); }
boost::function<void (void)> f;
};
struct Y : public X
{
Y() {}
Y(Callback &c) { c.add_listener(*this); }
virtual void go() { cout << "\t'" << __PRETTY_FUNCTION__ << "'" << endl; }
};
int main(void)
{
CallbackReal c_x;
CallbackReal c_y;
X x(c_x);
Y y(c_y);
cout << "Should be 'X'" << endl;
boost::bind<void>(boost::mem_fn(&X::go), x)();
cout << "Should be 'Y'" << endl;
boost::bind<void>(boost::mem_fn(&X::go), y)();
cout << "------------------" << endl;
cout << "Should be 'X'" << endl;
c_x.go();
cout << "I wish it were 'Y'" << endl;
c_y.go();
return 0;
}
Okay, I did not describe the problem completely. The title is misleading.
Oh, man. Downvote this one. I obviously haven't described the problem well and I think this ultimately boils down to mostly a syntactical error. :(
boost::bind takes its parameters by value and copies them. That means
f = boost::bind<void>(boost::mem_fn(&X::go), x);
will pass a copy of x, which will slice off the Y piece of it (if it was really a Y to begin with). To get virtual dispatch to work, you need to pass a pointer to boost::bind:
f = boost::bind(&X::go, &x);
(Note that you don't actually need mem_fn, or to explicitly write <void>, since boost::bind and argument deduction take care of those for you.)
Java interfaces don't specifically exist within C++. Closest you can get is pure abstract base classes. This is generally quite close enough.
The rest of your question is unrelated to interfaces. Java uses the Observer pattern for event connection and dispatch. The interface part is only mildly related because observers are required to obey specific interfaces (of course, since otherwise you wouldn't have any idea what to call).
Using boost::bind to create functors is actually an abstraction beyond interfaces and is thus a more generic solution. The observer pattern and functors are put together into signal/slot idiom/patterns implemented in various libraries like boost::signals, boost::signals2, and gtk++. The Qt version is quite different in mechanics but similar in concept.
So, what's this mean to help you understand what, why and where? I'd suggest starting with a search on what the Observer pattern is and try to write a few implementations.