News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Interfacing a c++ class object to masm

Started by hmi222, September 27, 2010, 10:25:47 AM

Previous topic - Next topic

hmi222

Hi!
I ve a problem to interface a static lib which contains a normal c++ class, not COM object.
i know there's something about the vtable, but not how to use with static lib.
I hope that anyone can help to go this way.
thanks in advance...

the c++ class looks looks like this:

//-----------------------------------------------------------------------------
// Copyright (c) 2007 dhpoware. All Rights Reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------

#if !defined(MODEL_OBJ_H)
#define MODEL_OBJ_H

#include <cstdio>
#include <map>
#include <string>
#include <vector>

//-----------------------------------------------------------------------------
// Alias|Wavefront OBJ file loader.
//
// This OBJ file loader contains the following restrictions:
// 1. Group information is ignored. Faces are grouped based on the material
//    that each face uses.
// 2. Object information is ignored. This loader will merge everything into a
//    single object.
// 3. The MTL file must be located in the same directory as the OBJ file. If
//    it isn't then the MTL file will fail to load and a default material is
//    used instead.
// 4. This loader triangulates all polygonal faces during importing.
//-----------------------------------------------------------------------------

class ModelOBJ
{
public:
    struct Material
    {
        float ambient[4];
        float diffuse[4];
        float specular[4];
        float shininess;        // [0 = min shininess, 1 = max shininess]
        float alpha;            // [0 = fully transparent, 1 = fully opaque]

        std::string name;
        std::string colorMapFilename;
        std::string bumpMapFilename;
    };

    struct Vertex
    {
        float position[3];
        float texCoord[2];
        float normal[3];
        float tangent[4];
        float bitangent[3];
    };

    struct Mesh
    {
        int startIndex;
        int triangleCount;
        const Material *pMaterial;
    };

    ModelOBJ();
    ~ModelOBJ();

    void destroy();
    bool import(const char *pszFilename, bool rebuildNormals = false);
    void normalize(float scaleTo = 1.0f, bool center = true);
    void reverseWinding();

    // Getter methods.

    void getCenter(float &x, float &y, float &z) const;
    float getWidth() const;
    float getHeight() const;
    float getLength() const;
    float getRadius() const;

    const int *getIndexBuffer() const;
    int getIndexSize() const;

    const Material &getMaterial(int i) const;
    const Mesh &getMesh(int i) const;

    int getNumberOfIndices() const;
    int getNumberOfMaterials() const;
    int getNumberOfMeshes() const;
    int getNumberOfTriangles() const;
    int getNumberOfVertices() const;

    const std::string &getPath() const;

    const Vertex &getVertex(int i) const;
    const Vertex *getVertexBuffer() const;
    int getVertexSize() const;

    bool hasNormals() const;
    bool hasPositions() const;
    bool hasTangents() const;
    bool hasTextureCoords() const;

private:
    void addTrianglePos(int index, int material,
        int v0, int v1, int v2);
    void addTrianglePosNormal(int index, int material,
        int v0, int v1, int v2,
        int vn0, int vn1, int vn2);
    void addTrianglePosTexCoord(int index, int material,
        int v0, int v1, int v2,
        int vt0, int vt1, int vt2);
    void addTrianglePosTexCoordNormal(int index, int material,
        int v0, int v1, int v2,
        int vt0, int vt1, int vt2,
        int vn0, int vn1, int vn2);
    int addVertex(int hash, const Vertex *pVertex);
    void bounds(float center[3], float &width, float &height,
        float &length, float &radius) const;
    void buildMeshes();
    void generateNormals();
    void generateTangents();
    void importGeometryFirstPass(FILE *pFile);
    void importGeometrySecondPass(FILE *pFile);
    bool importMaterials(const char *pszFilename);
    void scale(float scaleFactor, float offset[3]);

    bool m_hasPositions;
    bool m_hasTextureCoords;
    bool m_hasNormals;
    bool m_hasTangents;

    int m_numberOfVertexCoords;
    int m_numberOfTextureCoords;
    int m_numberOfNormals;
    int m_numberOfTriangles;
    int m_numberOfMaterials;
    int m_numberOfMeshes;

    float m_center[3];
    float m_width;
    float m_height;
    float m_length;
    float m_radius;

    std::string m_directoryPath;

    std::vector<Mesh> m_meshes;
    std::vector<Material> m_materials;
    std::vector<Vertex> m_vertexBuffer;
    std::vector<int> m_indexBuffer;
    std::vector<int> m_attributeBuffer;
    std::vector<float> m_vertexCoords;
    std::vector<float> m_textureCoords;
    std::vector<float> m_normals;

    std::map<std::string, int> m_materialCache;
    std::map<int, std::vector<int> > m_vertexCache;
};

//-----------------------------------------------------------------------------

inline void ModelOBJ::getCenter(float &x, float &y, float &z) const
{ x = m_center[0]; y = m_center[1]; z = m_center[2]; }

inline float ModelOBJ::getWidth() const
{ return m_width; }

inline float ModelOBJ::getHeight() const
{ return m_height; }

inline float ModelOBJ::getLength() const
{ return m_length; }

inline float ModelOBJ::getRadius() const
{ return m_radius; }

inline const int *ModelOBJ::getIndexBuffer() const
{ return &m_indexBuffer[0]; }

inline int ModelOBJ::getIndexSize() const
{ return static_cast<int>(sizeof(int)); }

inline const ModelOBJ::Material &ModelOBJ::getMaterial(int i) const
{ return m_materials; }

inline const ModelOBJ::Mesh &ModelOBJ::getMesh(int i) const
{ return m_meshes; }

inline int ModelOBJ::getNumberOfIndices() const
{ return m_numberOfTriangles * 3; }

inline int ModelOBJ::getNumberOfMaterials() const
{ return m_numberOfMaterials; }

inline int ModelOBJ::getNumberOfMeshes() const
{ return m_numberOfMeshes; }

inline int ModelOBJ::getNumberOfTriangles() const
{ return m_numberOfTriangles; }

inline int ModelOBJ::getNumberOfVertices() const
{ return static_cast<int>(m_vertexBuffer.size()); }

inline const std::string &ModelOBJ::getPath() const
{ return m_directoryPath; }

inline const ModelOBJ::Vertex &ModelOBJ::getVertex(int i) const
{ return m_vertexBuffer; }

inline const ModelOBJ::Vertex *ModelOBJ::getVertexBuffer() const
{ return &m_vertexBuffer[0]; }

inline int ModelOBJ::getVertexSize() const
{ return static_cast<int>(sizeof(Vertex)); }

inline bool ModelOBJ::hasNormals() const
{ return m_hasNormals; }

inline bool ModelOBJ::hasPositions() const
{ return m_hasPositions; }

inline bool ModelOBJ::hasTangents() const
{ return m_hasTangents; }

inline bool ModelOBJ::hasTextureCoords() const
{ return m_hasTextureCoords; }

#endif
 

redskull

Problem #1: The names will be incredibly mangled, and done in a vendor specific way; this means you will have to go to great pains to import them correctly (or export them in a different way)

Problem #2: C++ functions need a pointer to the data area you reserve for it (the 'this' pointer), usually passed in ecx (but again, vendor specific)

Problem #3: You only get a vTable if you are using virtual functions, which apparently you are not.

Problem #4: Your constructors and destructors will not be automatically called

Problem #5: You'll have to reserve your own space, so you'll have to convert your entire class definition to find out how much

Frankly, doing this is more trouble than it's worth.  If you really need to use a C++ class outside of C++, use a COM object; that's what they're there for.

-r
Strange women, lying in ponds, distributing swords, is no basis for a system of government

ToutEnMasm


Better way to do this:
Take a sample in c++ and made it translate in asm (/Fa) by the IDE
c++ express edition can do this

Then you can made a translate for masm


vozzie

Hy,

This can work too, it is "more"  maintainable and more easy then COM...

"Wrap the sequence of all the C++ functions you need to call into one(or more) procedures in a "C" library(A C++ library project that exports C procedures). Then call that/those library procedure(s) from your assembly project/code..."

Greets

hmi222

thanx for yur replies.

I think like vozzie say its more easy to produce "c" funktions of it. the next problem is that it must be flexible
functions in case of using more calls (more objects) of the funktions. so i need to produce flexible objects returning
pointers to call it in asm.

 

redskull

Quote from: hmi222 on September 28, 2010, 07:57:09 AMi need to produce flexible objects returning pointers to call it in asm.

If only someone had already come up with a way of writing language-neutral objects that could be used in binary form...

http://www.microsoft.com/com/default.mspx

:toothy

-r
Strange women, lying in ponds, distributing swords, is no basis for a system of government

hmi222

redskull:
i know what yu mean. but its not for interfacing new classes, im going to write. there are a lot of libs and codes who are already written.
an there for i need the interface.

redskull

Whether or not you make it into an "official" COM object, you will probably end up using a COM-like approach anyway.  Instead of using the C-style wrappers, I would recommend a single C-style "CreateObject" function to do just that, and then a C++ virtual base class to wrap the 'real' class (create a new class with matching functions, but no members, which are all pure virtual), and then derive the existing class from that one.  Then you can use the the object pointer to table pointer to function pointer trick, and don't have to worry about writing a new wrapper function everytime it changes.
-r
Strange women, lying in ponds, distributing swords, is no basis for a system of government

hmi222

@redskull :can yu give a small example please?

redskull

There are no "small" examples of something like this, only delicate, hacky ones I recommend against using.  Here is a given class:

class bar{
    public:
         bar(void) { cout << "I am a default constructor!" << endl;}
    void f(void) { cout << "I am a function!" << endl; }
   
};



First things first, we wrap it in an abstract base class:

class foo {
    public:
    virtual void f(void)=0;
};

class bar: public foo{
    // Same as before
};


Next, we need a factory function.  Notice it returns a 'bar' as a 'foo', and it's exported in the DLL with C-style linkage.

extern "C" __declspec(dllexport) foo* CreateFoo(void) { return new bar();}

And, of course, a default DLLMain (which should probably do more in a real implementation.)


BOOLEAN WINAPI DllMain( IN HINSTANCE hDllHandle,
         IN DWORD     nReason,
         IN LPVOID    Reserved )
{ return TRUE; }


Now, all that gets compiled into a DLL.  Onto the MASM side:

First, we need to import the library we generated, and declare the function.  Notice we import as C-style linkage, as well:


includelib  \minicom.lib
CreateFoo PROTO C


Now, we create the object, and get the back the pointer. 


invoke CreateFoo


This will create the object, and you'll see the constructor print it's output.  Now we have to go about calling the function, which is stored in the vtable, pointed to by the vptr (usually the first member).  We dereference it a few times, and then call it.  Again, note this is vendor specific, and liable to change.


mov ecx,eax       
mov eax,[ecx]
mov eax,[eax+0]
call eax


This would print the output of f().  You would alter the '+0' by intervals of 4 to call each virtual function.

Hopefully this has convinced you of what a bad idea this really is.  I leave it to to figure out a way of deleting the objects, and all the other esoteric problems that go hand in hand with this.

-r

Strange women, lying in ponds, distributing swords, is no basis for a system of government

mitchi

Why do you wrap it in an abstract class?

redskull

It's (IMO) the easiest way to call the functions.  Otherwise, you need to go to great lengths to import the hopelessly mangled names, or create C-style wrapper functions for every single one (taking into account the object pointer, as well).

-r
Strange women, lying in ponds, distributing swords, is no basis for a system of government