mono / CppSharp

Tools and libraries to glue C/C++ APIs to high-level languages
MIT License
3.05k stars 500 forks source link

Static members fail to generate reasonable bindings #654

Open JSandusky opened 8 years ago

JSandusky commented 8 years ago
Brief Description

Given this code containing a static const for "zero" (last real line of code):

I apologize, post height is a bit large, but still fell small enough that I felt inline was okay.

Input C++ Header

#pragma once

#include <SprueEngine/Math/MathDef.h>
#include <SprueEngine/Math/VectorMath.h>

namespace SprueEngine
{
    struct Mat3x3;

    struct Quat
    {
        float x, y, z, w;

        Quat() { x = y = z = w = 0.0f; }
        Quat(float w, float x, float y, float z) : x(x), y(y), z(z), w(w) {

        }
        Quat(const Vec3& dir, const Vec3 axis)
        {
            Vec3 forward = dir.Normalized();
            Vec3 v = forward.Cross(axis).Normalized();
            Vec3 up = v.Cross(forward);
            Vec3 right = up.Cross(forward);

            Quat ret;
            ret.FromAxes(right, up, forward);
            (*this) = ret;
        }

        Quat(const Mat3x3& mat);

        void Normalize()
        {
            float lenSquared = LengthSquared();
            if (!SprueEquals(lenSquared, 1.0f) && lenSquared > 0.0f)
            {
                float invLen = 1.0f / sqrtf(lenSquared);
                w *= invLen;
                x *= invLen;
                y *= invLen;
                z *= invLen;
            }
        }

        float LengthSquared() const { return w * w + x * x + y * y + z * z; }

        void FromAxes(const Vec3& right, const Vec3& up, const Vec3& forward)
        {
            float matrix[3][3] = { right.x, up.x, forward.x,
                right.y, up.y, forward.y,
                right.z, up.z, forward.z };

            float t = matrix[0][0] + matrix[1][1] + matrix[2][2];

            if (t > 0.0f)
            {
                float invS = 0.5f / sqrtf(1.0f + t);

                x = (matrix[2][1] - matrix[1][2]) * invS;
                y = (matrix[0][2] - matrix[2][0]) * invS;
                z = (matrix[1][0] - matrix[0][1]) * invS;
                w = 0.25f / invS;
            }
            else
            {
                if (matrix[0][0] > matrix[1][1] && matrix[0][0] > matrix[2][2])
                {
                    float invS = 0.5f / sqrtf(1.0f + matrix[0][0] - matrix[1][1] - matrix[2][2]);

                    x = 0.25f / invS;
                    y = (matrix[0][1] + matrix[1][0]) * invS;
                    z = (matrix[2][0] + matrix[0][2]) * invS;
                    w = (matrix[2][1] - matrix[1][2]) * invS;
                }
                else if (matrix[1][1] > matrix[2][2])
                {
                    float invS = 0.5f / sqrtf(1.0f + matrix[1][1] - matrix[0][0] - matrix[2][2]);

                    x = (matrix[0][1] + matrix[1][0]) * invS;
                    y = 0.25f / invS;
                    z = (matrix[1][2] + matrix[2][1]) * invS;
                    w = (matrix[0][2] - matrix[2][0]) * invS;
                }
                else
                {
                    float invS = 0.5f / sqrtf(1.0f + matrix[2][2] - matrix[0][0] - matrix[1][1]);

                    x = (matrix[0][2] + matrix[2][0]) * invS;
                    y = (matrix[1][2] + matrix[2][1]) * invS;
                    z = 0.25f / invS;
                    w = (matrix[1][0] - matrix[0][1]) * invS;
                }
            }
        }

        Vec3 operator*(const Vec3& rhs) const
        {
            Vec3 qVec(x, y, z);
            Vec3 cross1(qVec.Cross(rhs));
            Vec3 cross2(qVec.Cross(cross1));
            return rhs + ((cross1 * w + cross2) * 2.0f);
        }

        static const Quat Zero;
    };

}

The output source to CLI is (note, the CPP file is the meaningful one here):

CLI Output Header file

#pragma once

#include "CppSharp.h"
#include <SprueEngine/Math/Quaternion.h>

namespace SprueBind
{
    ref class Mat3x3;
    ref class Quat;
    ref class Vec3;
}

namespace SprueBind
{
    public ref class Quat : ICppInstance
    {
    public:

        property ::SprueEngine::Quat* NativePtr;
        property System::IntPtr __Instance
        {
            virtual System::IntPtr get();
            virtual void set(System::IntPtr instance);
        }

        Quat(::SprueEngine::Quat* native);
        static Quat^ __CreateInstance(::System::IntPtr native);
        Quat();

        Quat(float w, float x, float y, float z);

        Quat(SprueBind::Vec3^ dir, SprueBind::Vec3^ axis);

        Quat(SprueBind::Mat3x3^ mat);

        Quat(SprueBind::Quat^ _0);

        ~Quat();

        property float x
        {
            float get();
            void set(float);
        }

        property float y
        {
            float get();
            void set(float);
        }

        property float z
        {
            float get();
            void set(float);
        }

        property float w
        {
            float get();
            void set(float);
        }

        void Normalize();

        float LengthSquared();

        void FromAxes(SprueBind::Vec3^ right, SprueBind::Vec3^ up, SprueBind::Vec3^ forward);

        static SprueBind::Vec3^ operator*(SprueBind::Quat^ __op, SprueBind::Vec3^ rhs);

        static property SprueBind::Quat^ Zero
        {
            SprueBind::Quat^ get();
        }

        protected:
        bool __ownsNativeInstance;
    };
}

Output CLI CPP file (only problem section)

SprueBind::Quat^ SprueBind::Quat::Zero::get()
{
    auto __::SprueEngine::Quat::Zero = new ::SprueEngine::Quat(::SprueEngine::Quat::Zero);
    return (__::SprueEngine::Quat::Zero == nullptr) ? nullptr : gcnew SprueBind::Quat((::SprueEngine::Quat*)__::SprueEngine::Quat::Zero);
}

That source output in the CPP isn't sane (nor compilable). The variable name is invalid and the ternary really doesn't make any sense.

Easy to work around though using a special #ifndef and adding that to the driver options as a define.

OS: Windows Target: MSVC 2013, C++11, CLI output

JSandusky commented 8 years ago

I also encountered a loosely similar issue with functions returning std::string being expanded into std::basic_string() (without type args) for return as a System::String^, only mentioning because from a superficial look it might be related.

I resolved that just by post-processing the code because all cases were completely identical in output.

Most functions wanted to print as (good):

System::String^ SprueBind::SpruePiece::GetName()
{
    auto __ret = ((::SprueEngine::SpruePiece*)NativePtr)->GetName();
    return clix::marshalString<clix::E_UTF8>(__ret);
}

But a few printed as (bad):

System::String^ SprueBind::Deserializer::GetName()
{
    auto &__ret = ((::SprueEngine::Deserializer*)NativePtr)->GetName();
    return (System::String^)((&__ret == nullptr) ? nullptr : gcnew std::basic_string((::std::basic_string*)&__ret));
}

Header for the example good case: std::string GetName() const { return name_; }

Header for the example bad case: std::string ReadString();

The cleanup code used (to make things dandy):

if (lines[i].Contains("gcnew std::basic_string((::std::basic_string*)&__ret))"))
                    lines[i] = lines[i].Replace("gcnew std::basic_string((::std::basic_string*)&__ret))", "gcnew System::String(__ret.c_str()))");

Again, having not investigated, unsure of any interrelation.