Solved

State Design Pattern in C++ "base class undefined"?

Posted on 2013-11-07
8
480 Views
Last Modified: 2013-11-11
This works so easy in C#. Translating to C++ befuddles me.

The C++ compiler starts with file ConcreteStateA.h (I think it goes in alphabetical order) and it's complaining 'StateBase' : base class undefined

ConcreteStateA.h
#pragma once
#include "StateBase.h"

class ConcreteStateA : public StateBase
{
	public:
		virtual void Handle(MyContext *context) override;

};

Open in new window

But I have #include StateBase.h which is where class StateBase is defined. So why isn't this working?

StateBase.h
#pragma once
#include "MyContext.h"

class StateBase
{
	public:
		virtual void Handle(MyContext *context) = 0;
};

Open in new window

MyContext.h
#pragma once
#include "StateBase.h"
#include "ConcreteStateA.h"
#include "ConcreteStateB.h"

class MyContext
{
	private:
		class StateBase *_state;
	public:
		ConcreteStateA *a;
		ConcreteStateB *b;

		void Request();

		const StateBase &getState() const;
		void setState(const StateBase &value);

	private:
		void InitializeInstanceFields();
};

Open in new window

0
Comment
Question by:deleyd
  • 3
  • 3
  • 2
8 Comments
 
LVL 32

Assisted Solution

by:sarabande
sarabande earned 251 total points
Comment Utility
the problem is that mycontext.h is including statebase.h and statebase.h is including mycontext.h.

obviously that is a circular inclusion.

concretestatea.h
    includes statebase.h
       includes mycontext.h
            tries to include statebase.h but fails because of pragma once            
            tries to include concretestatea.h but fails because of pragma once
            includes concretestateb.h
                 .... ?
            defines class MyContext
                    fails to use StateBase *_state member cause class StateBase is not declared yet.

the way out is to include only in hierarchical order. you can declare pointers of a class where the header is not included by using a forward declaration:

// mycontext.h

// forward declaration
class StateBase;

class MyContext
{
      // pointer and reference of StateBase can be used
      StateBase  * pStateBase;
      ...

Open in new window


Sara
0
 
LVL 28

Assisted Solution

by:pepr
pepr earned 249 total points
Comment Utility
The StateBase class declares the method
virtual void Handle(MyContext *context) = 0;

Open in new window

as abstract one. It means there is no implementation. The derived class must define the body of the method.

The override reveals that the code follows the C++11 standard. It says the base class must declare the same virtual function (i.e. with the same signature), and that the derived class must implement it.

Update: Sara is right.
0
 
LVL 32

Assisted Solution

by:sarabande
sarabande earned 251 total points
Comment Utility
This works so easy in C#. Translating to C++ befuddles me.
actually it is the C precompiler which is responsible for the difference.

header files are included sequentially in c and c++ and can be controlled by macros (or pragma statements). the concept cannot be abandoned as long as c++ is a superset of c.

Sara
0
 

Author Comment

by:deleyd
Comment Utility
Forward Declarations. I see that's the concept I was missing.

I'm thinking the # pragma once would take care of the circular references?

I'm making progress. Still having some C++ syntax problems. I removed the override keyword. What does the virtual keyword mean in that same line? (Line 7 below)

ConcreteStateA.h
#pragma once
#include "StateBase.h"

class ConcreteStateA : public StateBase
{
	public:
		virtual void Handle(MyContext *context);
};

Open in new window

StateBase.h (Note I commented out #include "MyContext.h" and replaced with Forward Declaration
#pragma once
//#include "MyContext.h"

class MyContext;

class StateBase
{
	public:
		virtual void Handle(MyContext *context) = 0;
};

Open in new window

MyContext.h
#pragma once
#include "StateBase.h"
#include "ConcreteStateA.h"
#include "ConcreteStateB.h"

//forward declaration
class StateBase;

class MyContext
{
  public:
  MyContext();

	private:
		class StateBase *_state;
	public:
		ConcreteStateA *a;
		ConcreteStateB *b;

		void Request();

		const StateBase &getState() const;
		void setState(const StateBase &value);

	private:
		void InitializeInstanceFields();
};

Open in new window

MyContext.cpp
#include "StdAfx.h"
#include "MyContext.h"
#include "StateBase.h"
#include "ConcreteStateA.h"
#include "ConcreteStateB.h"

MyContext::MyContext(void)
{
		InitializeInstanceFields();
		_state = a;
}

	void MyContext::Request()
	{
		_state->Handle(this);
	}

	const StateBase &MyContext::getState() const
	{
		return _state;
	}

	void MyContext::setState(const StateBase &value)
	{
		_state = value;
	}

	void MyContext::InitializeInstanceFields()
	{
		a = new ConcreteStateA();
		b = new ConcreteStateB();
	}

Open in new window

Line 20 above is giving me error cannot convert from 'StateBase *const ' to 'const StateBase &'
Simmilarly, line 25 is giving me error cannot convert from 'const StateBase' to 'StateBase *'

ConcreteStateA.cpp
#include "StdAfx.h"
#include <iostream>
#include <string>
#include "ConcreteStateA.h"
#include "MyContext.h"


	void ConcreteStateA::Handle(MyContext *context)
	{
		std::cout << std::wstring(L"Handle called from ConcreteStateA") << std::endl;
		//context.State = new ConcreteStateB();
		context->setState(context->b);
	}

Open in new window

Line 10 above is giving me error about the << symbol.
Line 12 is giving me error: cannot convert from 'ConcreteStateB *' to 'const StateBase' in reference to the context->b
0
Better Security Awareness With Threat Intelligence

See how one of the leading financial services organizations uses Recorded Future as part of a holistic threat intelligence program to promote security awareness and proactively and efficiently identify threats.

 
LVL 28

Assisted Solution

by:pepr
pepr earned 249 total points
Comment Utility
On virtual methods (informally).

A method of the object is a function that can be applied to the object itself and to the passed arguments. That should be clear enough.

If both base and derived classes define the same kind of method (same signature, i.e. the same name and arguments), then the base class defines its own body for the method, and the derived class may define another one. When the object of the derived class exists and it calls its own method, it will use the code defined in the derived class. It can also explicitly call the code of the same method from the base class. In other words, a different code for both methods exists in parallel and can be used. However, implicitly, the object uses the code of its own class and ignores the code of the base class. We name it overloading.

What is important, the base class has no chance to know the method code of the derived class. The base class is more general, and there may be more of the specialized classes, each one overloading the code of the method differently. Anyway "it would be good if another method of the base class could call the well known method that would behave depending on the specialization". To do that, the base-class code have to learn what is the correct code to be called. It should get a pointer to the code, and the pointer should be directed to the code of the specialized class.

There is a table of such "pointers" and the table is called a virtual table. The keyword virtual says to the compiler that exactly this method should be threated the above described way. Virtual methods are the ones where even the base class  methods know how to call the specialized functionality.
0
 

Author Comment

by:deleyd
Comment Utility
Wow sounds complicated; yet it makes sense, most of it. So just keep the virtual keyword there I guess.

How about those 4 lines giving me syntax errors?
0
 
LVL 28

Assisted Solution

by:pepr
pepr earned 249 total points
Comment Utility
For virtual, it is enough to use the keyword only in the base class. Anyway, it is recommended to use it also in the derived classes as a reminder.

For the errors, whenever you restrict something to const, you cannot use it as non-const (or offer it as non-const to be used in another code). The compiler does not allow it. For the quick hack, you can remove the const modifier. However, if it is not your code, you should probably better think about the reasons.
0
 
LVL 32

Accepted Solution

by:
sarabande earned 251 total points
Comment Utility
I'm thinking the # pragma once would take care of the circular references?

Open in new window


the #pragma once makes sure that a header file is not included twice. by doing that it also prevents from circular including. but that doesn't handle the order of inclusions what is crucial in c/c++ as symbols (like class names) only can be used if they were declared or defined before.

alternatively to the #pragma you can do

#ifndef HEADER_H
#define HEADER_H
...
// put here the contents of the header file
...
#endif

Open in new window

what has the advantage that it is portable.

Wow sounds complicated; yet it makes sense, most of it. So just keep the virtual keyword there I guess.
virtual functions is the key principle of c++ and other object-oriented languages. it makes it possible to derive from an existing class an enhance that class by writing new or additional code for all functions where the design of the base class allows doing so. so the designer of a (possible) base class wouldn't add the virtual to all functions but only to those where they want allow a customization or where they want to promote a further specialization.

Sara
0

Featured Post

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

When writing generic code, using template meta-programming techniques, it is sometimes useful to know if a type is convertible to another type. A good example of when this might be is if you are writing diagnostic instrumentation for code to generat…
Container Orchestration platforms empower organizations to scale their apps at an exceptional rate. This is the reason numerous innovation-driven companies are moving apps to an appropriated datacenter wide platform that empowers them to scale at a …
The viewer will learn how to pass data into a function in C++. This is one step further in using functions. Instead of only printing text onto the console, the function will be able to perform calculations with argumentents given by the user.
The viewer will learn how to user default arguments when defining functions. This method of defining functions will be contrasted with the non-default-argument of defining functions.

771 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

11 Experts available now in Live!

Get 1:1 Help Now