C++ circular dependency with header guards and forward declarations

83 Views Asked by At

I have a simple Vec3 classand a simple xMath class.

The xMath class is supposed to change the Vec3. There are just 2 files Vec3.hpp and xMath.hpp (and I am not allowed to create other files).

  • both classes are in the same namespace
  • there are header guards
  • there are includes on both of them
  • xMath is not recognized
  • using forward declarations does not work, tried

Using Visual Studio 2019 Community Edition.

Getting errors:

  • error C2027 use of undefined type 'ns::xMath'Vec3.hpp

Using the forward declarations, still getting errors:

  • error C2653 Vec3 is not a class or a namespace name
  • error C2027 use of undefined type 'ns::Vec3'

Here are the files.

//Vec3.hpp
#ifndef VEC3_H
#define VEC3_H

#include "xMath.hpp"

namespace ns
{
    //class xMath; //forward declaration does not work either..

    struct Vec3
    {
    public:

        float x;

        //constructor
        Vec3(float x) { this.x = x; }

        static float Change(Vec3* v)
        {
            v->x = xMath::Change(v);

            return t;
        }

    };
}

#endif
//xMath.hpp
#ifndef XMATH_H
#define XMATH_H

#include "Vec3.hpp"

namespace ns
{
    //class Vec3; //forward declaration does not work either..

    static class xMath
    {
    public:

        static float Change (Vec3* v)
        {
            return v->x;
        }
    };
}

#endif
2

There are 2 best solutions below

0
Toby Speight On BEST ANSWER

Those requirements are very strange. Normally we'd put the implementation in its own file that includes both headers.

We can do something similar within the headers, though:

#ifndef XMATH_H
#define XMATH_H

// First the declarations:

namespace ns
{
    class Vec3;

    class xMath
    {
    public:
        static float Change (Vec3* v);
    };
}


// Now the implementation (which would normally be in xMath.cpp):

#include "Vec3.hpp"

float ns::xMath::Change (Vec3* v)
{
    return v->x;
}

#endif
0
SmellyCat On

At least part of the reason why the forward declarations were failing in the previous example was because Vec3 was forward-declared as a class but declared fully as a struct. The forward-declarations need to be consistent with the actual ones.

Error C2027 occurs against code that is accessing members of an class when only forward declaration is available. There are existing questions from 2009, 2015 and 2017.

With a forward declaration, you can instantiate pointers or references to the forward-declared class. You can't instantiate the class itself and you can't access its members.

Like Toby Speight and Paul Sanderson have suggested, the way to have both classes access each others' members is to implement the classes' methods outside of each class. The sequence is forward declarations, class declarations and finally implementations.

Normally, you would have the method implementations in one or two CPP files and those CPP files would include both header files. If you must use headers only, you can put the include statements after the class definitions.

A forward declaration like this does compile:

namespace ns
{
    class XMath;

    class Vec3
    {
    public:
        float m_x;

        Vec3(float x)
                : m_x(x)
                {}