22

I am relatively new to C++, and I have looked a lot for an answer for this thing but I never got a satisfying answer.

Let's say I have a structure called FSM. Eventually in my code, multiple instances of FSM can be created. One of FSM's attributes is int X which is not static, every instance of FSM should have its own value for X.

Now, one of FSM's attributes is another structure submachine which needs to read the value of X like this:

struct FSM
{
  public:
    int x;

    int getX(){return x;}

    struct submachine
    {
        void onentry() {int g = getX();};
    };
};

This gives the following error:

Error: 'FSM::getX' : illegal call of non-static member function

My question is, submachine is a member of FSM, so shouldn't it have access to local instances of all the attributes of FSM? And if not, when we create an instance of FSM, wouldn't we be creating an instance of all its members i.e. submachine? And if so, then why do we need to create an object that onentry() needs?

I am assuming the compiler is correct, so I would also want to know if there's a way to make this work.

NOTE: Unfortunately, the instances of the inner structures (submachine) are instantiated when an event is called and thus I can only define the type, and not instantiate objects for them in FSM.

Fraser
  • 74,704
  • 20
  • 238
  • 215
Kam
  • 5,878
  • 10
  • 53
  • 97

4 Answers4

40

my question is, submachine is a member of FSM, so it should have access to local instances of all the attributes of FSM, no?

No. Unlike in Java, inner class objects don't have an implicit reference to an outer object.

wouldn't we be creating an intance of all its members i.e. submachine?

submachine is a type, not a member variable. If you wanted a member variable, you'd have to do something like this:

struct FSM {
    struct submachine {
        ...
    };

    submachine sm;  // Member variable of type submchine
};

And if you want sm to "see" its parent object, you'll need to pass it explicitly:

struct FSM {
    struct submachine {
        FSM &parent;  // Reference to parent
        submachine(FSM &f) : parent(f) {}  // Initialise reference in constructor
    };

    submachine sm;

    FSM() : sm(*this) {}  // Pass reference to ourself when initialising sm
};

Note that the same principle applies for instances of submachine that aren't member variables. If you want them to be able to access an FSM instance, you'll need to pass a reference to one.

Note also that you could use a pointer rather than a reference. In fact, a pointer offers greater flexibility in many cases.

Oliver Charlesworth
  • 267,707
  • 33
  • 569
  • 680
  • if there's a way that I accomplish what I want to accomplish? – Kam Jul 10 '12 at 00:30
  • (Sorry for the delay, was finding the link). Nested classes are members and can access the outer class like any other members, see [DR 45](http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#45), part of the standard since 2003 – Jonathan Wakely Jul 10 '12 at 00:35
  • I have updated my question and I have explained why I can't instanciate an object in FSM, is there another way? – Kam Jul 10 '12 at 00:36
  • @JonathanWakely: Perhaps I phrased this badly; they don't have an implicit reference to the parent object. I will edit... – Oliver Charlesworth Jul 10 '12 at 00:36
  • 1
    @Kam: You will need to instantiate a `submachine` *somewhere* though? How will that be done? How will it be tied to a particular `FSM` object? – Oliver Charlesworth Jul 10 '12 at 00:38
  • Ah, yes, it's not at all clear that's what you meant, since nested classes _do_ get special access to the enclosing class. Will undo the downvote. The rest of the answer (explaining type not object) is well described. – Jonathan Wakely Jul 10 '12 at 00:38
  • @Oli I am using boost msm library, so basically the sumachine is a state that gets instanciated I am assuming inside the library. I am sorry I haven't mentioned that before – Kam Jul 10 '12 at 00:41
  • 1
    @Kam: Ok. Well the same principle applies. Whenever you construct a `submachine` instance, you will need to pass it a reference to an `FSM` instance. (However, I'm not familiar with that Boost library, so perhaps there's something about the API that makes this tricky.) – Oliver Charlesworth Jul 10 '12 at 00:42
  • @Oli, I am sure they do that, they have to, cause they say that the library is thread safe and all that so I am assuming they are keeping different intanced of the submachine, one for each instance of FSM. but I still don't know how I can do about it now? – Kam Jul 10 '12 at 00:44
  • 1
    @Kam: I don't understand a word you just said. You should make new question – Mooing Duck Jul 10 '12 at 00:45
  • @Kam: Like I say, I'm not familiar with Boost MSM, so I can't offer any specific advice about how to use that library. – Oliver Charlesworth Jul 10 '12 at 00:45
  • That trick where you pass, to a child, a reference/pointer back to the parent; I've sometimes seen that idiom being referred to as *back pointer* or *back reference*. I don't know if it's a widely recognized term, though. – Emile Cormier Jul 10 '12 at 00:45
  • 1
    I found a note in the Boost.MSM docs on state machine (and submachine) construction - hopefully it makes more sense to the OP than it does to me – Useless Jul 10 '12 at 00:57
3

consider that in your example, I can legally write a free function

void foo()
{
    FSM::submachine sub;
    sub.onentry();
}

where there is no FSM instance that sub could refer to.

Either, as Oli says, have the submachine object store a reference to its parent FSM object, or perhaps just pass the value of x directly into onentry (it isn't clear how it gets invoked).


From a quick look at the Boost.MSM docs I found this note on non-default-constructed submachines.

It's pretty ugly, I don't understand the backend enough to paraphrase it here, and the literal code won't make enough sense in isolation to be worth pasting.

The example code linked from there also shows the submachine's entry method with the following signature:

template <class Event,class FSM> void on_entry(Event const&,FSM& );

if that's accurate, you can either store a pointer to your outer state machine on_entry, or extract the value of x there and record it in the submachine.

Useless
  • 64,155
  • 6
  • 88
  • 132
  • Thank you! This is what I was looking for, now I actually get it. this post with OLI's post made it for me. I wish I can accept both answers. – Kam Jul 10 '12 at 01:10
2

Note that declaring struct submachine only defines the type; it does not actually create a field in the class of that type.

You would need one of the following:

struct submachine mysub; // creates a field after the class is defined

or

struct submachine
{
  . . .
} mysub; // creates the field "mysub" also, as the structure is being defined

This makes mysub the field, and you then access it in the same way that you'd access x.

The definition of submachine needs to include a specific FSM (e.g. a pointer field FSM*, and probably a constructor like submachine(FSM* fsm): fsm_(fsm) {} to initialize it) so that you can say fsm_->getX() to access a certain X value.

Kevin Grant
  • 5,363
  • 1
  • 21
  • 24
  • Unfortunately, the instances of the inner structures are declared runtime (events) and thus I can only define the type, and not instanciate objects for them. – Kam Jul 10 '12 at 00:32
  • 2
    You still can't do `int g = getX();` without an `FSM` object. How does this answer the question? – Jesse Good Jul 10 '12 at 00:35
1

I am only making a guess at what you want to do, but if my guess is correct, you might be thinking of something like the below.

struct FSM_Base {
    int x;

    struct submachine1;
    struct submachine2;

    FSM_Base () : x(0) {}
    virtual ~FSM_Base () {}
};

struct FSM_Base::submachine1 : virtual public FSM_Base {
    void oneentry () { int g = x; }
};

struct FSM_Base::submachine2 : virtual public FSM_Base {
    void oneentry () { int g = x; }
};

struct FSM : public FSM_Base::submachine1,
             public FSM_Base::submachine2 {
    FSM_Base::submachine1 * sub1 () { return this; }
    FSM_Base::submachine2 * sub2 () { return this; }
};
jxh
  • 69,070
  • 8
  • 110
  • 193