1

I have encountered a problem, which is most likely caused by a wrong forward declaration of circular dependent classes. But forward declaring template classes (similar to here seems to still not work.

With visual studio express edition 2013 I get a LNK 4221 warning (no new symbols defined) which causes LNK 2019 (unresolved external symbol).

Here are the headers that cause problems:

3d vector

#ifndef VEC3_H
#define VEC3_H

// Standard library
#include <iostream>
#include <sstream>

// Internal
#include "SMath.h"
#include "Quaternion.h"

namespace Sapling {
    template <typename T>
    class Quaternion; ///< Forward

    template <typename T> class Vec3 {
    public:
        // ...
        void rotate(Vec3<T> axis, T radAngle) {
        T sinHalfAngle = Math::sin(radAngle / 2);
        T cosHalfAngle = Math::cos(radAngle / 2);

        // Make a new quaternion [cos(w)|sin(w) vec]
        Quaternion<T> rotation(axis.x * sinHalfAngle,
                               axis.y * sinHalfAngle,
                               axis.z * sinHalfAngle,
                               cosHalfAngle);
        // Conjugate the rotation to eliminate the imaginary part
        Quaternion<T> conjugate = rotation.getConjugate();

        Quaternion<T> result = conjugate * (*this) * rotation; ///< frtl

        x = result.x;
        y = result.y;
        z = result.z;
        }
        // ...

        T x;
        T y;
        T z;
    };
}
#endif

Quaternion

#ifndef QUATERNION_H
#define QUATERNION_H

// Standard library
#include <iostream>
#include <sstream>

// Internal
#include "Vec3.h"

namespace Sapling {
    template <typename T>
    class Vec3; ///< Forward
    template <typename T> class Quaternion {
    public:
        // ...
        Quaternion(Vec3<T> vec, T W) : x(vec.x), y(vec.y), z(vec.z), w(W) { }
        // ...
        // Relational vector operators
        void operator*= (const Vec3<T>& v) {
            x = -(x * v.x) - (y * v.y) - (z * v.z);
            y = (w * v.x) + (y * v.z) - (z * v.y);
            z = (w * v.y) + (z * v.x) - (x * v.z);
            w = (w * v.z) + (x * v.y) - (y * v.x);
        }
        // ...

        T x;
        T y;
        T z;
        T w;
    };
}
#endif ///< Include guard

I know the data of both classes should be private, but I couldn't get around to fix it so far...

So could you explain to me why this still results in circular dependencies?
Thanks and have a nice day :)

Community
  • 1
  • 1
  • I don't see where your `Vec3` uses `Quaternion`... – Petr May 25 '15 at 12:11
  • What you have is a circular *include file* dependency. You try to break the circular bit by forward declarations, but you *still* have the circular inclusion going on. Also, if the `Vec3.h` header file you show is the complete actual file, then it doesn't really seem to be using the `Quaternion` class. – Some programmer dude May 25 '15 at 12:12
  • also class templates must be implemented in a header file – Piotr Skotnicki May 25 '15 at 12:13
  • Ok thank you for the responses. And Petr it uses quaternion for Vec3's rotation. – redpandamonium May 25 '15 at 12:15
  • @0lakan0, then you did not post the most important part of the code. I suggest you'd better remove all unrelated code such as operators, etc, but post the exact place where `Vec3` requires a `Quaternion`. – Petr May 25 '15 at 12:23
  • Ok I edited the posted code @Petr – redpandamonium May 25 '15 at 12:33
  • Unless you are doing weird things with the preprocessor, circular `#include` directives are always wrong. Just remove one of the two circular ones. I don't see how this can lead to *linker* errors though. – n. m. could be an AI May 25 '15 at 12:43
  • @n.m so how to let both of them be aware of each other? – redpandamonium May 25 '15 at 12:44
  • You must work with forward declarations. Put all declarations into one single header. If you can't get it to work, separating it into two headers won't help you. If you can, you can cut the file in two and it will still work. – n. m. could be an AI May 25 '15 at 12:54

1 Answers1

0

As a simple solution I can suggest splitting Quaternion into two classes, QuaternionBase which does not require Vec3, and Quaternion itself that subclasses QuaternionBase and introduces the constructor from Vec3:

// Vec3.h
...
#include <QuaternionBase.h>
...
template <typename T> class Vec3 {
public:
    // ...
    void rotate(Vec3<T> axis, T radAngle) {
        ...
        QuaternionBase<T> rotation(axis.x * sinHalfAngle,

// QuaternionBase.h
template <typename T> class QuaternionBase {
public:
    // all operations expect the constructor accepting Vec3

// Quaternion.h
#include <QuaternionBase.h>
#include <Vec3d.h>
template <typename T> class Quaternion : public QuaternionBase {
public:
    // ...
    Quaternion(Vec3<T> vec, T W) : QuaternionBase(vec.x, vec.y, vec.z, W) { }

Another solution is to make a base class for Vec3d and move there the access operations (.x etc).

A third solution is to make the Quaternion(Vec3, T) constructor templated like following

template<typename Vec>
Quaternion(Vec vec, T W) : x(vec.x), y(vec.y), z(vec.z), w(W) { }

hoping that this will not introduce additional ambiguity in other parts of the code.

Petr
  • 9,812
  • 1
  • 28
  • 52