Search code examples
c++dllimportdllexport

error Definition of dllimport function not allowed at one specific union while other classes, structs and unions get exported as they should


I am working on a game engine and try to export an union but somehow keep getting the following error:

Definition of dllimport function not allowed

Now I know what this error means and have solved it before but in this case I can't seem to find the answer.

I declare dllexport/dllimport in like this:

// Core.h

#ifdef CH_PLATFORM_WINDOWS

#ifdef CH_BUILD_DLL
#define CH_API __declspec(dllexport)
#else 
#define CH_API __declspec(dllimport)
#endif // CH_BUILD_DLL

#else 
#error Cheetah currently only supports windows!

#endif // CH_PLATFORM_WINDOWS

I use this macro on multiple places already and these classes, structs and unions get exported/imported as they should, per example:

// Vector4.h

#ifndef CHEETAH_ENGINE_MATH_VECTOR4_H_
#define CHEETAH_ENGINE_MATH_VECTOR4_H_

#include "Core/Core.h"
#include "Vector3.h"

namespace cheetah
{
    template<typename T>
    union CH_API Vector4
    {
        inline Vector4();
        inline Vector4(const T& fill);
        inline Vector4(const T fill[4]);
        inline Vector4(const Vector3<T>& fill, const T& w);
        inline Vector4(const T& x, const T& y, const T& z, const T& w);

        struct
        {
            T x, y, z, w;
        };

        inline const T* get() const;

        inline T magnitude() const;

        inline void operator *= (const T& rhs);
        inline void operator += (const T& rhs);
        inline void operator -= (const T& rhs);
        inline void operator /= (const T& rhs);

        inline Vector4<T> operator + (const Vector4<T>& rhs) const;
        inline Vector4<T> operator - (const Vector4<T>& rhs) const;

        inline T operator * (const Vector4<T>& rhs) const;

    private:
        struct
        {
            T m_data[4];
        };
    };

    template union CH_API Vector4<float>;
    template union CH_API Vector4<int>;
    template union CH_API Vector4<double>;

    using Vector4f = Vector4<float>;
    using Vector4i = Vector4<int>;
    using Vector4d = Vector4<double>;
}

#include "Vector4.inl"

#endif // !CHEETAH_ENGINE_MATH_VECTOR_H_

But somehow the union Quaternion doesn't get exported even though it is very much the same as the Vector4 union above.

// Quaternion.h
#ifndef CHEETAH_CORE_MATH_QUATERNION_H_
#define CHEETAH_CORE_MATH_QUATERNION_H_

#include "Vector4.h"
#include "Vector3.h"
#include "Mat4x4.h"

#include<math.h>

#include "Core/Core.h"

namespace cheetah
{
    template<typename T>
    union CH_API Quaternion
    {
    public:
        Quaternion();
        Quaternion(const T& axisX, const T& axisY, const T& axisZ, const T& degrees);
        Quaternion(const Vector3<T>& axis, const T& degrees);
        Quaternion(const T fill[4]);

        struct
        {
            T axisX, axisY, axisZ, degrees;
        };

        inline const T* get() const;
        inline Mat4x4<T> getMatrix() const;

        inline void normalize();
        inline Quaternion<T> normalize(const Quaternion<T>& vector) const;

        inline void operator *= (const T& rhs);
        inline void operator += (const T& rhs);
        inline void operator -= (const T& rhs);
        inline void operator /= (const T& rhs);

    inline Quaternion<T> operator + (const Vector4<T>& rhs) const;
    inline Quaternion<T> operator - (const Vector4<T>& rhs) const;

    inline T operator * (const Vector4<T>& rhs) const;

    private:
        struct 
        {
            T m_data[4];
        };
    };

    template union CH_API Quaternion<float>;
    template union CH_API Quaternion<int>;
    template union CH_API Quaternion<double>;

    using Quaternionf = Quaternion<float>;
    using Quaternioni = Quaternion<int>;
    using Quaterniond = Quaternion<double>;
}

#include "Quaternion.inl"

#endif // !CHEETAH_CORE_MATH_QUATERNION_H_

The error gets thrown on all constructors of the Quaternion union in the implementation file Quaternion.inl that contains all the implementations of Quaternion.

// Quaternion.inl
namespace cheetah 
{
    template<typename T>
    inline Quaternion<T>::Quaternion()
        : m_data{ 0, 0, 0, 0 }
    {
    }

    template<typename T>
    inline Quaternion<T>::Quaternion(const T& axisX, const T& axisY, const T& axisZ, const T& 
    degrees)
        : m_data{ axisX, axisY, axisZ, degrees }
    {
    }

    template<typename T>
    inline Quaternion<T>::Quaternion(const Vector3<T>& axis, const T& degrees)
        : m_data{ axis.x, axis.y, axis.z, degrees }
    {
    }

    template<typename T>
    inline Quaternion<T>::Quaternion(const T fill[4])
        : m_data{ fill[0], fill[1], fill[2], fill[3] }
    {
    }
}

I haven't included all implementations because it is a lot of lines, if more is needed let me know in the comments. Also I forward declared the specializations I want to export of both unions in a.cpp file.

I can't see what the difference is between the vector4 and the quaternion union and why the Vector4 gets exported but the Quaternion not.

What I have tried:

I tried to remove all class specialization methods from the .inl file of Quaternion because that is one of the few differences with the Vector4 union, The Quaternion has some specialization members like below:

template<>
inline void Quaternion<float>::normalize()
{
    const float n = 1.0f / sqrt(axisX * axisX + axisY * axisY + axisZ * axisZ + degrees * degrees);
    m_data[0] *= n;
    m_data[1] *= n;
    m_data[2] *= n;
    m_data[3] *= n;
}

this still results in the error.

Somehow the CH_API macro keeps getting expanded to dllimport(is also shown in the syntax highlighting) while this isn't the case with the Vector4 union.


Solution

  • As said in the comments the union didn't need to be decorated with dllexport, by removing the CH_API macro it worked. I still export the template specializations.

    template<typename T>
    union CH_API Quaternion
    

    The above code needed to be changed to:

    template<typename T>
    union Quaternion
    

    And then I could export the template specializations like this:

    template union CH_API Quaternion<float>;
    template union CH_API Quaternion<int>;
    template union CH_API Quaternion<double>;
    

    I haven't been able to find out why the CH_API gets expanded to dllimport in this specific case though and to dllexport with the Vector4 union, I might update my answer if I find out why that is happening.