0

I have one class "A" that contains some field, one of these fiels is a class "Z", so I want to send data from this field(which is a class) to the class "A", when I tried to send data from the class "A" to the "Z" via reference it worked, but now I can't see how can I process data in the reverse way. here's the code:

#ifndef SCREEN_AGENDAVIEW_HPP
#define SCREEN_AGENDAVIEW_HPP

#include <gui_generated/screen_agenda_screen/Screen_agendaViewBase.hpp>
#include <gui/screen_agenda_screen/Screen_agendaPresenter.hpp>
#include <string>

class Screen_agendaView : public Screen_agendaViewBase
{
public:
    Screen_agendaView();
    virtual ~Screen_agendaView() {}
    void Open_Container()override;
    void SendToView(std::string text);
protected:
    CustomContainer_event ce;
};

#endif // SCREEN_AGENDAVIEW_HPP
#include <gui/screen_agenda_screen/Screen_agendaView.hpp>

Screen_agendaView::Screen_agendaView():ce(*this)
{

}
void Screen_agendaView::Open_Container()
{
    customContainer_event1.setVisible(true);
    customContainer_event1.invalidate();
}
void Screen_agendaView::SendToView(std::string text)
{
    Unicode::snprintf(textArea1Buffer, TEXTAREA1_SIZE, "%s", text);
    textArea1.setWildcard(textArea1Buffer);
    textArea1.invalidate();
}
#ifndef CUSTOMCONTAINER_EVENT_HPP
#define CUSTOMCONTAINER_EVENT_HPP

#include <gui_generated/containers/CustomContainer_eventBase.hpp>
#include <gui/screen_agenda_screen/Screen_agendaView.hpp>
#include <string>


class CustomContainer_event : public CustomContainer_eventBase
{
public:
    //CustomContainer_event();
    CustomContainer_event(Screen_agendaView& d);
    virtual ~CustomContainer_event() {}

    virtual void initialize();
    void Save_Note()override;


protected:
    int pos_cursor = 0, shif = 1;
    Screen_agendaView& s;
    std::string text;
};

#endif // CUSTOMCONTAINER_EVENT_HPP
CustomContainer_event::CustomContainer_event(Screen_agendaView& s):s(s)
{

}

void CustomContainer_event::initialize()
{
    CustomContainer_eventBase::initialize();
    flexButton_Shift.setPressed(true);
}

void CustomContainer_event::Save_Note()
{
    for (int i = 0; i <= pos_cursor; i++)
        text += textArea2Buffer[i];
    s.SendToView(text);
}
Backik
  • 11
  • 4
  • 3
    "fields" == member variables? Please, expose a [mcve] to improve your question. – Scheff's Cat May 07 '20 at 09:10
  • yes fields == member variables – Backik May 07 '20 at 09:11
  • 1
    You could pass `class A`'s `this` (as pointer) or `*this` (as reference) to the constructor of member var. of type `class Z`. Hence, `class Z` could store this pointer or reference as member itself and use it for accessing its "parent" `class A` instance. – Scheff's Cat May 07 '20 at 09:14
  • do you mean something like that: class Z z=new class Z(this);? and how can I use it the class Z? – Backik May 07 '20 at 09:18
  • 1
    Sorry, that's _no_ [mcve]. Remove all noise and condense your sample to the absolute minimum to reproduce your issue. – Scheff's Cat May 07 '20 at 10:00
  • Btw. what's your issue with the exposed code? Doesn't it compile? Doesn't it run as expected? – Scheff's Cat May 07 '20 at 10:02
  • it doesn't compile, the things that I added when you told me to add cause Error compile – Backik May 07 '20 at 10:04
  • Classes which refer to each other had to `#include` each others headers but this leads to [circular header dependencies](https://stackoverflow.com/q/625799/7478597). The solution is that one of the involved classes has to use a [forward declaration](https://en.wikipedia.org/wiki/Forward_declaration) instead. Forward declarations form an incomplete type. It can be used to declare pointers or references (but not much more). Have a look again at my answer. I remarked these details in comments. – Scheff's Cat May 07 '20 at 10:07
  • yes but the error is this: 'ce': unknown override specifier – Backik May 07 '20 at 10:12
  • Please, read carefully what I wrote in my answer. I believe I covered all issues of your situation. If you still don't get your stuff compiled, please, practice with a smaller (separate) prototype first until you become aware of all the little pitfalls. (This is what I do in daily business as well. To be efficient, it's always recommend to break problems down to handable sizes. Making prototypes for isolated problems is one kind of it.) – Scheff's Cat May 07 '20 at 10:28

1 Answers1

0

OP could pass class A's this (as pointer) or *this (as reference) to the constructor of the member var. of type class Z. Hence, class Z could store this pointer or reference as member itself and use it for accessing its "parent" class A instance.

Demo:

#include <iostream>

struct A; // forward declaration

struct Z {
  A &a; // Z holds a reference to the associated instance of A

  Z(A &a): a(a) { } // the associated instance of A is passed in constructor

  void call(); // cannot be implemented before A became a complete type
};

struct A {
  Z z; // A has a member var. of `struct Z` to which it will be associated

  A(): z(*this) { } // A passes *this (the reference of its own) to z

  void callZ()
  {
    std::cout << "A::callZ() called.\n";
    z.call();
  }

  void call()
  {
    std::cout << "A::call() called.\n";
  }
};

// now A is complete and Z::call() can use it to access (public) members of A &a

void Z::call()
{
  std::cout << "Z::call() called.\n";
  a.call();
}

int main()
{
  A a;
  a.callZ();
}

Output:

A::callZ() called.
Z::call() called.
A::call() called.

Live Demo on coliru

A class which stores a reference to another instance doesn't come without dangers. When such relations are designed then the (human) author should have the intended life-times in mind.

In this case, struct Z will be used as member of struct A exclusively. So, the associated instance of A will outlive it's member z.

If an instance of a class stores the reference to another instance which is destroyed before the first then the reference (inside the first) becomes dangling (i.e. referencing something which isn't existing anymore). As long as the reference isn't used nothing bad will happen. But accessing the dangling reference is Undefined Behavior. (It could crash or cause strange effects or (most accidental case) seem to work until something strange happens later.)

Example for wrong usage:

int main()
{
  A *pA = new A();
  Z z(*pA);
  z.call(); // That's OK.
  delete pA;
  z.call(); // :-O BAD! z will call z.a.call() although z.a became dangling!
}

This design may cause an additional issue because struct A has to know struct Z to access its members and struct Z has to know struct A. That's a hen-egg-problem and C++ isn't the only effected language.

The solution is a forward declaration, in the above sample

struct A; // forward declaration

A forward declaration forms an incomplete type. It can be used to declare pointers and references. Pointers and references of incomplete types may be used only with certain constraints. E.g. sizeof of the pointer to an incomplete type is allowed because it actually doesn't depend on the type itself. (Pointers have always the same size.) In opposition, accessing the pointee of an incomplete type is not possible (as the pointee is of incomplete type and its contents simply not known as long as the type is incomplete).

Circular dependencies may require that declaration and definition of member functions are separated. In the above sample, it is remarked for Z::call():

// now A is complete and Z::call() can use it to access (public) members of A &a

void Z::call()
{
  std::cout << "Z::call() called.\n";
  a.call();
}

Defining classes in headers (as it is usual in C++ projects) may cause the issue of circular header dependencies.

If a.h declares struct A and z.h declares struct Z one might be attempted to #include "z.h" in a.h as well as #include "a.h" in z.h. This doesn't work. Either this will lead to a recursion or the author was clever enough to use header guards (to prevent duplicated definitions). In the latter case, one of the two involved headers will finally try to use the declaration of the other class in its own declaration and fail.

The solution is again a forward declaration of one involved class in the header of the other instead of the resp. #include. Both C++ sources may then include both header files without harm and provide the implementations which are based on both classes. This will, of course, require that the implementation of the resp. methods isn't done inline but separately done in the C++ source.

Scheff's Cat
  • 19,528
  • 6
  • 28
  • 56
  • I don't know but it doesn't work, wanna see my code? – Backik May 07 '20 at 09:48
  • @Backik Have a look at the link [**Live Demo on coliru**](http://coliru.stacked-crooked.com/a/61a1f13f7f11c61c) - it does work. ;-) If it doesn't work in your case, please, edit your question and add a [mcve] (as I already requested). – Scheff's Cat May 07 '20 at 09:53
  • I edited it, you can take a look now CustomContainer is the class and the field of the screen_agenda – Backik May 07 '20 at 09:59
  • @Backik Pfew, that's a long answer now... ;-) – Scheff's Cat May 07 '20 at 10:25
  • I can't understand what's the meaning of forward declaration. can you explain it a little bit simply – Backik May 07 '20 at 10:31
  • @Backik Didn't you click the link, I provided? In my sample, `struct A;` is a forward declaration. It tells the compiler that `struct A` will be there without telling any detail. I thought I explained it in the last extension of my answer... – Scheff's Cat May 07 '20 at 10:33
  • yes but I didn't understand it, is it same as giving a prototype function to tell the compiler that this function exists? – Backik May 07 '20 at 10:37
  • 1
    @Backik Hmmm. Somehow comparable. Though, prototypes work because the **linker** will put stuff together. For a call of a function, the compiler doesn't need anything else than the prototype. For types it's a little bit different. Types are always evaluated by the **compiler**. So, you can give the compiler first just the existence of a type (by a forward declaration) which can already be used in a limited way to define other types. Later you have to add the complete definition before the type can be used fully. (Thereby, "first" and "later" refer to the order of lines read by the compiler.) – Scheff's Cat May 07 '20 at 10:41