2

I am creating an iPhone app using OpenGL ES 2, and it uses both objective-c and c++. In it there is a header file for defining some vector properties, and this file includes cmath which then causes the error.

Somebody suggested changing main.m to main.mm but this causes symbols not found for architecture errors(65 of them). However it does get rid of the cmath error. I have tried fiddling with which code type various files are compiled as but this does not help. Also, if I don't include Vector.h in my rendering engine then the cmath error goes away, but makes more errors.

This has happen on two projects now, that are of the same form.

Vector.h

#ifndef Robots_Vector_h
#define Robots_Vector_h

#include <cmath>

const float Pi = 4 * std::atan(1.0f);
const float TwoPi = 2 * Pi;

template <typename T>
struct Vector2 {
    Vector2() {}
    Vector2(T x, T y) : x(x), y(y) {}
    T Dot(const Vector2& v) const
    {
        return x * v.x + y * v.y;
    }
    Vector2 operator+(const Vector2& v) const
    {
        return Vector2(x + v.x, y + v.y);
    }
    Vector2 operator-(const Vector2& v) const
    {
        return Vector2(x - v.x, y - v.y);
    }
    void operator+=(const Vector2& v)
    {
        *this = Vector2(x + v.x, y + v.y);
    }
    void operator-=(const Vector2& v)
    {
        *this = Vector2(x - v.x, y - v.y);
    }
    Vector2 operator/(float s) const
    {
        return Vector2(x / s, y / s);
    }
    Vector2 operator*(float s) const
    {
        return Vector2(x * s, y * s);
    }
    void operator/=(float s)
    {
        *this = Vector2(x / s, y / s);
    }
    void operator*=(float s)
    {
        *this = Vector2(x * s, y * s);
    }
    void Normalize()
    {
        float s = 1.0f / Length();
        x *= s;
        y *= s;
    }
    Vector2 Normalized() const
    {
        Vector2 v = *this;
        v.Normalize();
        return v;
    }
    T LengthSquared() const
    {
        return x * x + y * y;
    }
    T Length() const
    {
        return sqrt(LengthSquared());
    }
    const T* Pointer() const
    {
        return &x;
    }
    operator Vector2<float>() const
    {
        return Vector2<float>(x, y);
    }
    bool operator==(const Vector2& v) const
    {
        return x == v.x && y == v.y;
    }
    Vector2 Lerp(float t, const Vector2& v) const
    {
        return Vector2(x * (1 - t) + v.x * t,
                       y * (1 - t) + v.y * t);
    }
    template <typename P>
    P* Write(P* pData)
    {
        Vector2* pVector = (Vector2*) pData;
        *pVector++ = *this;
        return (P*) pVector;
    }
    T x;
    T y;
};

template <typename T>
struct Vector3 {
    Vector3() {}
    Vector3(T x, T y, T z) : x(x), y(y), z(z) {}
    T Length()
    {
        return std::sqrt(x * x + y * y + z * z);
    }
    Vector3 Cross(const Vector3& v) const
    {
        return Vector3(y * v.z - z * v.y,
                       z * v.x - x * v.z,
                       x * v.y - y * v.x);
    }
    T Dot(const Vector3& v) const
    {
        return x * v.x + y * v.y + z * v.z;
    }
    Vector3 operator+(const Vector3& v) const
    {
        return Vector3(x + v.x, y + v.y,  z + v.z);
    }
    void operator+=(const Vector3& v)
    {
        x += v.x;
        y += v.y;
        z += v.z;
    }
    void operator-=(const Vector3& v)
    {
        x -= v.x;
        y -= v.y;
        z -= v.z;
    }
    void operator/=(T s)
    {
        x /= s;
        y /= s;
        z /= s;
    }
    Vector3 operator-(const Vector3& v) const
    {
        return Vector3(x - v.x, y - v.y,  z - v.z);
    }
    Vector3 operator-() const
    {
        return Vector3(-x, -y, -z);
    }
    Vector3 operator*(T s) const
    {
        return Vector3(x * s, y * s, z * s);
    }
    Vector3 operator/(T s) const
    {
        return Vector3(x / s, y / s, z / s);
    }
    bool operator==(const Vector3& v) const
    {
        return x == v.x && y == v.y && z == v.z;
    }
    Vector3 Lerp(float t, const Vector3& v) const
    {
        return Vector3(x * (1 - t) + v.x * t,
                       y * (1 - t) + v.y * t,
                       z * (1 - t) + v.z * t);
    }
    const T* Pointer() const
    {
        return &x;
    }
    void Normalize()
    {
        if (this->Dot(*this) != 0) {
            float s = 1.0f / Length();
            x *= s;
            y *= s;
            z *= s;
        }
    }
    /*Vector3 operator=(const Vector3& v) {
     x = v.x;
     y = v.y;
     z = v.z;
     return v;
     }*/
    Vector3 Normalized() const
    {
        Vector3 v = *this;
        v.Normalize();
        return v;
    }
    template <typename P>
    P* Write(P* pData)
    {
        Vector3<T>* pVector = (Vector3<T>*) pData;
        *pVector++ = *this;
        return (P*) pVector;
    }
    T x;
    T y;
    T z;
};

template <typename T>
struct Vector4 {
    Vector4() {}
    Vector4(T xarg, T yarg, T zarg, T warg) : x(xarg), y(yarg), z(zarg), w(warg) {}
    Vector4(const Vector3<T>& v, T w) : x(v.x), y(v.y), z(v.z), w(w) {}
    T Dot(const Vector4& v) const
    {
        return x * v.x + y * v.y + z * v.z + w * v.w;
    }
    Vector4 Lerp(float t, const Vector4& v) const
    {
        return Vector4(x * (1 - t) + v.x * t,
                       y * (1 - t) + v.y * t,
                       z * (1 - t) + v.z * t,
                       w * (1 - t) + v.w * t);
    }
    void operator+=(const Vector4& v) {
        x += v.x;
        y += v.y;
        z += v.z;
        w += v.w;
    }
    Vector4 operator*(T s) {
        return Vector4(x*s, y*s, z*s, w*s);
    }
    const T* Pointer() const
    {
        return &x;
    }
    T x;
    T y;
    T z;
    T w;
};

typedef Vector2<bool> bvec2;

typedef Vector2<int> ivec2;
typedef Vector3<int> ivec3;
typedef Vector4<int> ivec4;

typedef Vector2<float> vec2;
typedef Vector3<float> vec3;
typedef Vector4<float> vec4;

#endif

RenderingEngineES2.cpp

//
//  RenderingEngineES2.cpp
//  Robots
//
//  Created by James Thorneycroft on 01/07/2015.
//  Copyright (c) 2015 James Thorneycroft. All rights reserved.
//

#include <stdio.h>

#include <OpenGLES/ES2/gl.h>
#include <OpenGLES/ES2/glext.h>
#include "Interfaces.h"
#include "Matrix.h"
#include <iostream>

namespace ES2 {

#define STRINGIFY(A) #A
//#include "../Shaders/Simple.frag"
//#include "../Shaders/SimpleLighting.vert"
#include "../PixelLighting.vert"
#include "../Shaders/PixelLighting.frag"


    struct UniformHandles {
        GLuint Modelview;
        GLuint Projection;
        GLuint NormalMatrix;
        GLuint LightPosition;
        GLuint CameraAxis;
    };

    struct AttributeHandle {
        GLint Position;
        GLint Normal;
        GLint Ambient;
        GLint Diffuse;
        GLint Specular;
        GLint Shininess;
    };

    struct Drawable {
        GLuint VertexBuffer;
        GLuint IndexBuffer;
        int IndexCount;
    };

    class RenderingEngine : public IRenderingEngine {
    public:
        RenderingEngine();
        void Initialize(const vector<ISurface*>& surfaces);
        void Render(const vector<Visual>& visuals) const;

    private:
        GLuint BuildProgram(const char* vertexShaderSource, const char* fragmentShaderSource) const;
        GLuint BuildShader(const char* source, GLenum shaderType) const;

        vector<Drawable> m_drawables;
        GLuint m_depthRenderBuffer;
        GLuint m_colorRenderBuffer;

        UniformHandles m_uniforms;
        AttributeHandle m_attributes;
    };

    IRenderingEngine* CreateRenderingEngine() {
        return new RenderingEngine();
    }


    GLuint RenderingEngine::BuildShader(const char* source, GLenum shaderType) const
    {
        GLuint shaderHandle = glCreateShader(shaderType);
        glShaderSource(shaderHandle, 1, &source, 0);
        glCompileShader(shaderHandle);

        GLint compileSuccess;
        glGetShaderiv(shaderHandle, GL_COMPILE_STATUS, &compileSuccess);

        if (compileSuccess == GL_FALSE) {
            GLchar messages[256];
            glGetShaderInfoLog(shaderHandle, sizeof(messages), 0, &messages[0]);
            std::cout << messages;
            exit(1);
        }

        return shaderHandle;
    }

    GLuint RenderingEngine::BuildProgram(const char* vertexShaderSource,
                                         const char* fragmentShaderSource) const
    {
        GLuint vertexShader = BuildShader(vertexShaderSource, GL_VERTEX_SHADER);
        GLuint fragmentShader = BuildShader(fragmentShaderSource, GL_FRAGMENT_SHADER);

        GLuint programHandle = glCreateProgram();
        glAttachShader(programHandle, vertexShader);
        glAttachShader(programHandle, fragmentShader);
        glLinkProgram(programHandle);

        GLint linkSuccess;
        glGetProgramiv(programHandle, GL_LINK_STATUS, &linkSuccess);
        if (linkSuccess == GL_FALSE) {
            GLchar messages[256];
            glGetProgramInfoLog(programHandle, sizeof(messages), 0, &messages[0]);
            std::cout << messages;
            exit(1);
        }

        return programHandle;
    }

    RenderingEngine::RenderingEngine() {
        glGenRenderbuffers(1, &m_colorRenderBuffer);
        glBindRenderbuffer(GL_RENDERBUFFER, m_colorRenderBuffer);
    }

    void RenderingEngine::Initialize(const vector<ISurface *> &surfaces) {
        m_cameraPosition = vec4(0,0,0,0);
        vector<ISurface*>::const_iterator surface;
        for (surface = surfaces.begin(); surface != surfaces.end(); ++surface) {
            vector<float> vertices;
            (*surface)->GenerateVertices(vertices, VertexFlagNormals);
            GLuint vertexBuffer;
            glGenBuffers(1, &vertexBuffer);
            glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
            glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(vertices[0]), &vertices[0], GL_STATIC_DRAW);

            int indexCount = (*surface)->GetTriangleIndexCount();
            GLuint indexBuffer;
            if (!m_drawables.empty() && indexCount == m_drawables[0].IndexCount) {
                indexBuffer = m_drawables[0].IndexBuffer;
            } else {
                vector<GLushort> indices(indexCount);
                (*surface)->GenerateTriangleIndices(indices);
                glGenBuffers(1, &indexBuffer);
                glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
                glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexCount * sizeof(GLushort), &indices[0], GL_STATIC_DRAW);
            }

            Drawable drawable = { vertexBuffer, indexBuffer, indexCount };
            m_drawables.push_back(drawable);
        }

        int height, width;
        glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &width);
        glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &height);

        glGenRenderbuffers(1, &m_depthRenderBuffer);
        glBindRenderbuffer(GL_RENDERBUFFER, m_depthRenderBuffer);
        glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);

        GLuint framebuffer;
        glGenFramebuffers(1, &framebuffer);
        glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_colorRenderBuffer);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthRenderBuffer);
        glBindRenderbuffer(GL_RENDERBUFFER, m_colorRenderBuffer);

        GLuint program = BuildProgram(PixelLightingVertexShader, PixelLightingFragmentShader);
        glUseProgram(program);

        m_attributes.Position = glGetAttribLocation(program, "Position");
        m_attributes.Normal = glGetAttribLocation(program, "Normal");
        m_attributes.Ambient = glGetUniformLocation(program, "AmbientMaterial");
        m_attributes.Diffuse = glGetAttribLocation(program, "DiffuseMaterial");
        m_attributes.Specular = glGetUniformLocation(program, "SpecularMaterial");
        m_attributes.Shininess = glGetUniformLocation(program, "Shininess");
        m_uniforms.Projection = glGetUniformLocation(program, "Projection");
        m_uniforms.Modelview = glGetUniformLocation(program, "Modelview");
        m_uniforms.NormalMatrix = glGetUniformLocation(program, "NormalMatrix");
        m_uniforms.LightPosition = glGetUniformLocation(program, "LightPosition");
        m_uniforms.CameraAxis = glGetUniformLocation(program, "CameraAxis");

        glUniform3f(m_attributes.Ambient, 0.15f, 0.15f, 0.15f);
        glUniform3f(m_attributes.Specular, 0.15f, 0.15f, 0.15f);
        glUniform1f(m_attributes.Shininess, 100.0f);

        glEnableVertexAttribArray(m_attributes.Position);
        glEnableVertexAttribArray(m_attributes.Normal);
        glEnable(GL_DEPTH_TEST);

        //m_cameraOrientation.Identity();

    }

    void RenderingEngine::Render(const vector<Visual> &visuals) const {
        glClearColor(0, 0.3f, 0.6f, 1);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        vector<Visual>::const_iterator visual = visuals.begin();
        for (int visualIndex = 0; visual != visuals.end(); ++visual, ++visualIndex) {
            ivec2 size = visual->ViewportSize;
            ivec2 lowerLeft = visual->LowerLeft;
            glViewport(lowerLeft.x, lowerLeft.y, size.x, size.y);

            mat4 rotation = visual->Orientation.ToMatrix4();
            mat4 modelView = m_cameraOrientation.ToMatrix4() * mat4::Translate(vec3(m_cameraPosition.x,m_cameraPosition.y,m_cameraPosition.z-100)) * rotation * Quaternion::CreateFromAxisAngle(vec3(-1,0,0), 1.2f).ToMatrix4();
            glUniformMatrix4fv(m_uniforms.Modelview, 1, 0, modelView.Pointer());
            vec3 camAx = m_cameraOrientation.ToMatrix3() * visual->Orientation.ToMatrix3() * Quaternion::CreateFromAxisAngle(vec3(-1,0,0), 1.2f).ToMatrix3() * vec3(0,0,1);
            glUniform3f(m_uniforms.CameraAxis, camAx.x, camAx.y, camAx.z);

            vec4 lightPosition(0, 0, 1, 0);
            glUniform3fv(m_uniforms.LightPosition, 1, (modelView*lightPosition).Pointer());

            mat3 normalMatrix = modelView.ToMat3();
            glUniformMatrix3fv(m_uniforms.NormalMatrix, 1, 0, normalMatrix.Pointer());

            float h = 4.0f * size.y / size.x;
            mat4 projectionMatrix = mat4::Frustum(-2, 2, -h / 2, h / 2, 5, 1000);
            glUniformMatrix4fv(m_uniforms.Projection, 1, 0, projectionMatrix.Pointer());

            vec3 color = visual->Color * 0.75f;
            glVertexAttrib4f(m_attributes.Diffuse, color.x, color.y, color.z, 1);

            int stride = 2*sizeof(vec3);
            const GLvoid* offset = (const GLvoid*) sizeof(vec3);
            GLint position = m_attributes.Position;
            GLint normal = m_attributes.Normal;
            const Drawable& drawable = m_drawables[visualIndex];
            glBindBuffer(GL_ARRAY_BUFFER, drawable.VertexBuffer);
            glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, stride, 0);
            glVertexAttribPointer(normal, 3, GL_FLOAT, GL_FALSE, stride, offset);
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, drawable.IndexBuffer);
            glDrawElements(GL_TRIANGLES, drawable.IndexCount, GL_UNSIGNED_SHORT, 0);
        }
    }
}
James
  • 173
  • 3
  • 12
  • The correct approach is doing as "someone" said and use .mm for your file extension. The architecture errors you're receiving afterward are nearly-certainly because compilation *succeeded* and linking *failed* (which is a different issue entirely, likely because you haven't configured the toolkit you're trying to use correctly or an iPhone compatible framework isn't prepared). – WhozCraig Jul 06 '15 at 20:03
  • @WhozCraig Copied the code from a previous project so I forgot to link libraries. Quite weird the problem has only manifested just now, but thank you very much for the quick and correct answer. – James Jul 07 '15 at 00:05

2 Answers2

3

Instead of

#include <cmath>

use

#include <math.h>
Phil
  • 818
  • 5
  • 14
1

The problem here is that Objective-C works on top of C APIs, and <cmath> isn't available in C — <cmath> is a C++ include, with the C equivalent being <math.h> (as Phil pointed out).

The solution is to switch over to using Objective-C++, which works on top of C++ and gives you access to all of the functionality of C++ in your Obj-C classes, as well as letting you import C++ headers (such as yours).

Switching your Objective-C code to Objective-C++ is trivial— just rename any Objective-C implementation file that has the desired #include "Vector.h" to have a .mm extension instead of .m (e.g. MyRendererView.m is renamed to MyRendererView.mm; MyRendererView.h can remain as-is).  That's it!  All your existing Objective-C code will be able to use your Objective-C++ class without hassle; you only need to change the extension of Objective-C files including your Vector.h or exposing C++ types their their own headers.

There are a few minor naming caveats when using Objective-C++ instead of Objective-C that it's been too long for me to remember.  If you hit any weird errors, that's almost certainly a StackOverflow answer addressing it.  It'll probably come down to tweaking an Xcode build setting— the most important build settings to peek at are the “C Language Dialect” & “C++ Language Dialect” settings.

Slipp D. Thompson
  • 33,165
  • 3
  • 43
  • 43
  • What if it is a precompiled header file? – zwcloud Jul 23 '19 at 10:14
  • @zwcloud PCH files are generally expected to be include-able by both C & C++ _(if a project contains both C & C++ code; a project with only C or only C++ might not be written for both)_.  Older Xcode-generated PCHes would include conditional compilation like `#ifdef __cplusplus` `extern "C" {` `#endif`; I'm not sure if Xcode still adds that, but if it doesn't, you can add it yourself. The process for making your PCH both C & C++ compatible (using `extern` and other patterns) is well-documented elsewere, and not specific to Objective-C, Xcode, or macOS development. – Slipp D. Thompson Jul 27 '19 at 03:50
  • @zwcloud In your specific case, it sounds like you might just want `#ifdef __cplusplus` `#include ` `#else` `#include ` `#endif` in your PCH. – Slipp D. Thompson Jul 27 '19 at 03:51