interaction between GUIs

Windows 11, Visual Studio 2022, C++, GUI programming.

This project was created under Windows Desktop Application, but maybe one of the other project types. Some GUIs need to communicate with others, meaning call functions in the other. The only way I know how to do that is to start in one GUI, get the address of the other, create a pointer, then use that pointer. I suspect there is a much better way.

Here are some example names for this particular conversation. A GUI called Worker does an essential task. A GUI called Tester is to verify that the functions of Worker do work correctly. What is/are the best methods to call functions in Worker from Tester?

Just for completeness, in Tester I put an #include for a class in Worker, then tried to call a function in that class. The build tool reported: ‘clear_one_edit_control’: identifier not found.

Side question. I wanted to bold the words Worker and Tester. The words were highlighted, the B was clicked on in the tool bar at the bottom for each, the click Preview. From the Preview page I could not get back to my original question. What did I do wrong?
Last edited on
Some GUIs need to communicate with others, meaning call functions in the other

Are we talking about different windows (or views etc.) within the same process, or completely separate processes?

I'm going to assume you mean different (or views etc.) windows within the same process.

I think you should re-design your code, if your one window (or view etc.) needs to call a function from another. Instead, you should be following a Model-View-Controller (MVC) pattern. In short, if any "state" in your application needs to be updated, then the logic (function) to modify the state goes into the controller. The function in the controller manipulates the state in the model as needed. Finally, whenever the "state" in the model changes, all the view(s), i.e. the GUIs, that are connected to the model will updated themselves accordingly.

In other words, you generally don't call a function in one view (GUI) from another. Instead, from the GUI (e.g. user input) you invoke the suitable function in the controller, so that the controller can manipulate the "state" in the model. This will, in turn, allow the other views (GUIs) to update themselves as needed. This design provides a nice decoupling of all the views in your application!

https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller

____

How do the views (GUIs) know when something in the model has changed?

The observer pattern is your friend here:
https://en.wikipedia.org/wiki/Observer_pattern

Simply put, the model is the "subject" here, and the views (GUIs) are the "subscribers". This allows the views (GUIs) to subscribe to the model. So, whenever a change to the state in the model occurs, the model notifies all the views (GUIs).

____

Honstely, these days, you can simple enter a query like "Simple but complete Model-View-Controller example with pure Win32 API" into ChatGPT (or whatever LLMs you prefer) and that should give you something that gets your started.
Last edited on
I was completely unaware of MVC. This is not a web application and a complete re-write seems like tremendous overkill just to share access between GUIs. Seems like having separate GUIs for different purposes and being able to call each other's functions would be a common concept. I am looking into the MVC concept.

The Observer patten looks like a possibility so am looking there.

There are two other methods, I suspect. Each GUI gets a pointer to modules it needs, as I have done with two of them. It is a bit awkward, must be certain the other GUI already exists, but once the pointer is set it provides fast interface.

Or use a shared module, something I just discovered for Visual Studio and maybe other development systems for multiple GUIs. I found a description about that. Maybe use it to hold and share pointers to every GUI.

Thank you for your time.
Last edited on
MVC pattern is not specific to web-applications at all!

Also, it is just a pattern, not a finished code. So, you can implement whatever aspect of MVC pattern that you think is suitable for your application; you do not have to strictly adhere to the whole design. For example, a strict distinction between controller and model is not always necessary. But decoupling the views from the "backend" (core) is almost always a very good idea 😏

Anyway, if you have any sort of "state" that needs to be shared between multiple views (GUIs), or if you have some functions that need to be called from different views (GUIs), then the "shared" state and/or the "shared" functions should be moved to a separate "backend" module (class, etc. pp.) – instead of making one view (GUI) depend on implementation details of another one.

To make your views (GUIs) update themselves whenever the "state" in the shared backend (model, etc. pp.) changes, use the observer pattern, or use something like Signals & Slots, both of which allows you to nicely decouple the views from the core:
https://web.mit.edu/~firebird/arch/sun4x_58/doc/html/signalsandslots.html


There are two other methods, I suspect. Each GUI gets a pointer to modules it needs, as I have done with two of them. It is a bit awkward, must be certain the other GUI already exists, but once the pointer is set it provides fast interface.

I think there is nothing wrong with passing a pointer (or better, a reference!) to the singleton instance of the "core" (backend) into the constructor of each view (GUI). Each view (GUI) would then keep this reference to call methods on the "core" object as needed.

Something in the vein of:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
int main() {
    // Create a single backend instance
    Backend backend;

    // Create two views that share the same backend
    View view1(backend, "ViewA");
    View view2(backend, "ViewB");

    // Both views use the same backend instance
    view1.render();
    view2.render();

    return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class View {
public:
    // Constructor receives a reference to Backend
    View(Backend &backend, std::string viewName) : m_backend(backend) {
        /* ... */
    }

    void render() {
        /* ... */
        this->m_backend.some_function();
        /* ... */
    }

private:
    Backend &m_backend; // non-owning reference
};



However, I would strongly advise against passing a pointer (or reference) to one view (GUI) into another view! Such a design would make the views (GUIs) depend on each other, which usually is a really bad design choice.
Last edited on
All that I saw about MVC kept referencing web application. To me that implies and sounds like an intermediary between every component. I will look at that again.

Thanks for the new information.

This app will be doing long arithmetic. A primary example is the Mandlebrot set zoomed in further than ever done before. The arithmetic does 256 bit arithmetic to 512 bits of precision. A possibly long series of multiplication and additions for each pixel in the image. Up to maybe 1000 rounds for each pixel, at that resolution.

A long time ago, for my BSCS, I did a special course on programming in C, Turbo C. An incredible IDE for the day. I wrote an application to calculate and display the Mandlebrot set. It was with an 8086 and the separate math chip the 8087. I wrote the critical section in assembly code to directly access the 8087 and was able to get several more bits of resolution. There were some really incredible images there. Not a lot of resolution on the 1988 monitors compared with today, but incredible just the same. When zoomed in really deep, with every point running something like 500 and 1000 iterations, it would run almost all night long for one image.

Back to today, the interface between the part guiding the application and the part doing the individual multiplies and additions must be fast. There won’t be a lot of interfaces, but must be rather fast. That arithmetic part was more difficult than anticipated. Just for completeness I need to add a divide function. And this will be pure integer or fixed point fractions.

Regarding your point about pointers, each GUI will get a pointer to the ones it needs. No sharing of pointers. When I prove the concepts and the code, the arithmetic module can probably be moved into a simple class and forgo the GUI part. I need to figure out how to add in free standing classes in the GUI environment but suspect that will be relatively easy. For now the GUI has been helpful.

I will search and read some more. Thank you for your time and patience.
Last edited on
Just do not intermix the view (GUI) code with the code that implements your business logic (e.g. "arithmetic" operations).

Instead, put the "arithmetic" operations into a separate "backend" class/module/whatever where the view(s) can call it as needed.

For example, make the "backend" class singleton and give each view (GUI) a reference (pointer) to the single "backend" instance.

This way, each view (GUI) can call into the "backend" as needed, but none of the views needs to know about any other view!
Last edited on
kigar64551,
I have written code with classes, but this is my first time with GUIs and with Visual Studio GUIs.
After reading your posts and thinking, I realized that my GUI to actually do the long arithmetic could be a standard class. Starting with a fully working solution, I tried creating a simple class to see if it works. The new C_Long_Arithmetic.h file contains

1
2
3
4
5
6
7
8
9
#pragma once
class C_Long_Arithmetic
{
private:
   static const int test = 42;
public:
   int get_test();
 
 };


The new C_Long_Arithmetic.cpp contains

1
2
3
4
5
6
7
#include "pch.h"
#include "C_Long_Arithmetic.h"

int C_Long_Arithmetic::get_test() 
{
   return test;
}


In an existing and working function within the code for the GUI and named C_Do_Arithmetic, added

1
2
3
4
5
6
7
...
#include "C_Long_Arithmetic.h"
...
   int x = 0;
//   x = C_Long_Arithmetic::get_test();
   x = get_test();
...


The two errors are:

1
2
3
C3861 'get_test':identifier not found    C_Do_arithmetic.cpp 269
E0020 identifier "get_test is undefined  C_Do_arithmetic.cpp 269
 


When the upper line of the two new function calls is enabled the error codes are slightly different but roughly the same.

Just to be complete, when both the new lines of code are commented out, leaving the new .h and .cpp files present, the code builds with no errors.

Is this sufficient such that you can see my error?

Thank you for your time and patience.
Last edited on
Try something like this:

arithmetic.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#pragma once
#include <vector>
#include "view.h"

// The "backend" class, put your business logic here
class Arithmetic
{
public:
	void register_view(IView *view); // <-- register a new view
	int some_function(void);

private:
	std::vector<IView*> m_views; // <-- list of all registered views
	void notify_views(void); // <-- trigger update of *all* views
};


view.h
1
2
3
4
5
6
7
#pragma once

// The abstract base class for all views
class IView {
public:
	virtual void update(void) = 0;
};

view_a.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#pragma once
#include "view.h"
#include "arithmetic.h"

// Concrete view (GUI) implementation "A"
class View_A : IView
{
public:
	View_A(Arithmetic& arithmetic);
	virtual void update(void);

private:
	Arithmetic& m_arithmetic;
	void button_clicked_event(void);
};

view_b.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#pragma once
#include "view.h"
#include "arithmetic.h"

// Concrete view (GUI) implementation "B"
class View_B : IView
{
public:
	View_B(Arithmetic& arithmetic);
	virtual void update(void);

private:
	Arithmetic& m_arithmetic;
	void button_clicked_event(void);
};

arithmetic.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include "arithmetic.h"

void Arithmetic::register_view(IView *view) {
	m_views.push_back(view);
}

int Arithmetic::some_function(void) {
	return 42;
}

void Arithmetic::notify_views(void) {
	for (auto iter = m_views.cbegin(); iter != m_views.cend(); ++iter) {
		(*iter)->update();
	}
}

view_a.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
#include "view_a.h"

View_A::View_A(Arithmetic &arithmetic) : m_arithmetic(arithmetic) {
	arithmetic.register_view(this);
}

void View_A::update(void) {
	/* update the view here! */
}

void View_A::button_clicked_event(void) {
	m_arithmetic.some_function(); // <-- call function from Arithmetic instance
}

view_b.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
#include "view_b.h"

View_B::View_B(Arithmetic &arithmetic) : m_arithmetic(arithmetic) {
	arithmetic.register_view(this);
}

void View_B::update(void) {
	/* update the view here! */
}

void View_B::button_clicked_event(void) {
	m_arithmetic.some_function(); // <-- call function from Arithmetic instance
}

main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
#include "view_a.h"
#include "view_b.h"

int main(int argc, char* argv[]) {
	Arithmetic arithmetic;

	View_A view_a(arithmetic);
	View_B view_b(arithmetic);

	/* ... */

	return 0;
}
Last edited on
Wow! That is a lot of code to call one function.
This will take a while to understand.
Thank you for your time.
Wow! That is a lot of code to call one function.

Most of this you probably already have in your application (in one way or another) and you just need to adapt it 😏

Also, making all view (GUI) classes inherit from the same interface (base class), i.e., IView, so that they can be registered to the backend (Arithmetic) class, is not ab absolutely required. It just demonstrates how we can propagate "updates" from the backend to the views - without making the backend depend on the concrete view implementations. That's essentially the "observer" pattern.
Last edited on
I found the answer. In the new class, in the dot h file, declare all the functions and constants as static. The call becomes:

x = C_Long_Arithmetic::get_test();

It is as simple as that. And if you know about static.
That is because a static function belongs to the class, not to a concrete instance (object) of that class.

Using static functions is okay, for as long as you don't need to carry a per-instance (per-object) state in your C_Long_Arithmetic class.
Last edited on
Registered users can post here. Sign in or register to post.