C/C++ External dynamic library linkage -- example project?

  1. 2 years ago

    Stephen G

    13 Oct 2017 Pre-Release Testers, Xojo Pro Ellensburg, Washington

    I have a huge amount of non-UI C/C++ code that I need the Xojo app to access as an external library for Mac, Windows and IOS (dynamic, I'm assuming, though not sure I can do that on IOS).

    I've read up on Declare, but what would be a super-useful example Xojo project would be one that demonstrated simple two-way communication of intrinsic Xojo types and structures to and from an external library, combined with rudimentary C/C++ projects that are designed to be compiled 64-bit on macOS, Windows, and IOS.

    I'm just thinking about a single .h/.cpp file defining a trivial interface that demonstrates getting and setting values such as boolean, Integer, Double, String, Text, etc. I understand the actual API would need to be C rather than C++.

    Is there such a simple Xojo example project combined with a simple cross-platform buildable skeletal library?

    Stephen

  2. Dave S

    13 Oct 2017 San Diego, California USA

    I am no "C" expert, no a Xojo Declare one either...... but from what I have seen and used, Declares are designed to access a precompiled "API" via its exposed entry points, passing it a predefined set of parameters and or structures, and returning (if required) similar values.

    Not to interface with a "normal" "C" program.

    So given the published entry points of a public API, it should be (with the proper background) easy to determine what the "Declare" might need to say.

  3. Garth H

    13 Oct 2017 Pre-Release Testers, Xojo Pro

    I have a lot of experience with this. All the regular data types (except String) are very obvious to work with, it's hardly necessary for a big huge example.

    String and MemoryBlock and the other common ones. Best way to use those are to call a Ptr in the Declare and pass a MemoryBlock. A MB is natural to this; with String you'd convert it to a MemoryBlock (not hard), but it's important that you set the correct Encoding on the String first. Then on the C++ end you convert the memory to a String, and again it's important to know the Encoding. Mac wide strings are 4-byte while Windows wide strings are 2-byte. Or you could pass UTF-8 and convert it on the C++ side.

    As for structures and classes, I've found the best way is to create a MemoryBlock that holds all the properties etc. and pass that and convert it on the C++ side. My projects I code the important classes and structures twice - one of the Xojo side and one on the C++ side. There's no other way around it, and this is very efficient.

  4. Stephen G

    13 Oct 2017 Pre-Release Testers, Xojo Pro Ellensburg, Washington

    Garth, thanks --

    I believe the default for C++'s own internal libraries is to assume std::string as UTF-8, so that's probably the safest and easies encoding to work with.

    So -- in order to pass a UTF-8 encoded string TO the API, you're saying it MUST be first converted to a MemoryBlock? On the way OUT of the API, you're suggesting allocating a MemoryBlock and passing a pointer to that so the C++ can copy the UTF-8 into that pre-allotted buffer?

    Is there a difference between converting the MemoryBlock containing the UFT-8 to a String, versus a Text intrinsic?

    Stephen

  5. Stephen G

    13 Oct 2017 Pre-Release Testers, Xojo Pro Ellensburg, Washington

    @Dave S --

    but from what I have seen and used, Declares are designed to access a precompiled "API" via its exposed entry points, passing it a predefined set of parameters and or structures, and returning (if required) similar values.

    Not to interface with a "normal" "C" program.

    Dave -- my mistake if I wasn't clear: I wasn't thinking about connecting to a "program" with it's own main(). I have a huge investment in C++ cross-platform "model" ("business logic") code, and I don't want to rewrite all that in Xojo -- makes more sense to publish a "C" API into a library containing that logic (dylib on the mac, DLL on Windows. Not sure how that links on IOS -- I don't think iPhone apps can provide dynamic libraries).

    Stephen

  6. Eli O

    is not verified 13 Oct 2017 Europe (Berlin, Germany)

    You have two options:

    Plugins:
    Develop the API in C/C++ and C/Objective-C so that in Xojo you do not need any declares into dynamic libraries. See Plugins-SDK .

    Declares:

    @StephenGreenfield Is there such a simple Xojo example project combined with a simple cross-platform buildable skeletal library?

    First part of your question: no, there are no examples for Declares as Declares are just definitions for functions in dynamic libraries so that the Xojo framework / compiler knows about that. This is a simple translation of C parameter types and return types to the Xojo equivalents.
    Second part of your question: no, because such a dylib/dll is not Xojo specific at all.

    In general I would say it does not make sense to develop either one, if it is for a plugin or dynamic library which is project specific. The amount of work in both cases is very large and only makes sense if the plugin or dynamic library is reusable. You are probably better of with a C/C++ UI library like Qt or platform-specific UIs – or by re-writing everything in Xojo.

  7. Christian S

    14 Oct 2017 Pre-Release Testers, Xojo Pro, XDC Speakers, Third Party Store Germany

    You can pass UTF-8 Xojo String to your API function with CString data type in Xojo and a "const char*" in C.

  8. Norman P

    14 Oct 2017 Xojo Inc, Pre-Release Testers, Xojo Pro Seeking work. npalardy@great-w...

    yes there are examples
    Next to your copy of Xojo is Example Projects > Advanced > Declares
    While these don't declare into YOUR dll / dylib etc they do declare into ones that have plain C API's on Windows, Mac OS and Linux

    Secondly there are several projects that I know of the use Xojo as the UI and the underlying core logic is implemented in C/C++ and even assembler where necessary

  9. Stephen G

    14 Oct 2017 Pre-Release Testers, Xojo Pro Ellensburg, Washington

    @Eli O --

    In general I would say it does not make sense to develop either one, if it is for a plugin or dynamic library which is project specific. The amount of work in both cases is very large and only makes sense if the plugin or dynamic library is reusable. You are probably better of with a C/C++ UI library like Qt or platform-specific UIs – or by re-writing everything in Xojo.

    The library I'm referring to would have no platform-specific code, nor UI. So in the "Model-View-Controller" paradigm, we're talking entirely about model-specific code.

    We've needed to expose our API for a lot of other clients that want to make use of the underlying story/narrative computations. These make sense to preserve as C++ code in a library with "C" style API.

    When I was looking for a "skeletal" example program, it was really to just make sure I was certain on the basics of 1) the Xojo side of the interface, and 2) the required syntax, if any, on the DYLIB/DLL side.

  10. Stephen G

    14 Oct 2017 Pre-Release Testers, Xojo Pro Ellensburg, Washington

    @ChristianSchmitz -- Is there any way to initiate a callback from a dylib/dll into Xojo? If I'm following the Model-View-Controller architecture, if the model changes some data, I would want to be able to post notification to a Controller to get View data re-loaded.

  11. Christian S

    14 Oct 2017 Pre-Release Testers, Xojo Pro, XDC Speakers, Third Party Store Germany

    You can pass in ptr of delegate to c and call it.

    If needed we could schedule online or offline meeting to talk about this and get you started.

  12. Garth H

    14 Oct 2017 Pre-Release Testers, Xojo Pro

    I use a "callback" - I send the AddressOf of a Xojo function to the DLL, and call that.

    Regarding strings, since I use UTF-16 in my C++ code, I just do the MemoryBlock conversions because I know they work. I'm sure there is an easier way by actually passing strings, but I've been burned before in things changing. Just converting into a memory buffer (a blob) makes it easier and seemingly more robust.

    I'd highly recommend using your code as a library and use Xojo as a front end. It's served me very well.

  13. Christian S

    14 Oct 2017 Pre-Release Testers, Xojo Pro, XDC Speakers, Third Party Store Germany

    Depends on what you do.
    Memoryblick makes a copy.

    If you pass CString you pass pointer to temp string and put that in c++ into std::string you have a copy there to work with. Depends on what you need.

  14. Eli O

    is not verified 14 Oct 2017 Europe (Berlin, Germany)
    Edited 2 years ago

    @StephenGreenfield @Eli O --
    The library I'm referring to would have no platform-specific code, nor UI. So in the "Model-View-Controller" paradigm, we're talking entirely about model-specific code.

    What I meant is that the work to create a C API around your C++ library might be a lot of work – or even too much work –, and in such a case it might be too much work compared to a C++ UI. It depends on how large the API becomes.

    @StephenGreenfield We've needed to expose our API for a lot of other clients that want to make use of the underlying story/narrative computations. These make sense to preserve as C++ code in a library with "C" style API.

    This makes sense as this is a form of re-use as I mentioned.

    @StephenGreenfield When I was looking for a "skeletal" example program, it was really to just make sure I was certain on the basics of 1) the Xojo side of the interface, and 2) the required syntax, if any, on the DYLIB/DLL side.

    There is no such thing. Declares are telling Xojo the parameter and return type of the C function. Then you just use it. dylibs/dlls must be C libraries and be libraries for dynamic binding. That's it.

  15. Stephen G

    14 Oct 2017 Pre-Release Testers, Xojo Pro Ellensburg, Washington

    @Eli O --

    and in such a case it might be too much work compared to a C++ UI. It depends on how large the API becomes.

    I so wish there was a C++ "UI". Whenever my C++ guru friends tout C++17 (and future versions), I bemoan that C++ has useful cross-platform file system support (e.g. POSIX) but nothing when it comes to any built-in cross-platform abstraction for View implementation. I know, "how would that work?!" But like with Xojo, a menu is a menu, Window-View-Control hierarchies are more-or-less similar, there are rudimentary basic types of controls. Even making an attempt could simplify the development process.

    But I guess that's why you folks are here. And why I'm examining Xojo to see if it can suitably perform as the View-Controller to my C++ Model code.

    I guess I will CREATE a test project that exercises sending and receiving the intrinsic types to a TEST DYLIB/DLL. But I'll bet it won't be without missteps on my part.

  16. Eli O

    is not verified 14 Oct 2017 Europe (Berlin, Germany)

    If you know C – which you obviously do – it is not a real challenge (though some rare edge cases are in the beginning). It is just a tedious work if the C API is large.
    Declare
    Of course I do not know the internals of the Xojo compiler but as far as I understand it, you are directly calling the function in the library – there is no sending/receiving wrapper or the like build around a declare function built by the Xojo compiler.

  17. Stephen G

    21 Oct 2017 Pre-Release Testers, Xojo Pro Ellensburg, Washington

    So I'm trying to put together all the bits and pieces of suggestions, but running into some kind of very basic (no pun intended) problem. Probably a newbie mistake -- or several.

    My test throws an exception, that the function I'm calling can't be found, but nm shows the function IS in the dylib, with normal C-callable binding:

    Stephen-Retina-MacBookPro-59:Debug stephen$ nm libTestLibForXojo.dylib

    0000000000000f2c s GCC_except_table1
                     U __Unwind_Resume
    0000000000000de0 T __ZN10MyCPPClass13GetTestStringEv
    0000000000000ec0 t __ZN10MyCPPClassC1EPKc
    0000000000000ef0 t __ZN10MyCPPClassC2EPKc
                     U __ZdlPv
                     U __Znwm
                     U ___gxx_personality_v0
    0000000000000e60 T _deleteMyClass
    0000000000000ea0 T _getACStringFromClass
    0000000000000e00 T _newMyCPPClass
                     U dyld_stub_binder

    The test app is just the absolute minimum to construct a class, call a function from that class, get a returned string (in this case a static string) and destruct the class. The path to the dylib is absolute. A FunctionNotFoundException being thrown comes as I try to call newMyCPPClass(), below.

    XOJO TEST APP: This is the code on the action event of a button:

    CONST dylibLocation = "/Users/stephen/dev/XOJO Development/Tests for dynamic library communication/TestLibForXojo/DerivedData/Build/Products/Debug/libTestLibForXojo.dylib"
    
    soft declare function newMyCPPClass lib dylibLocation as Ptr
    soft declare function getACStringFromClass lib dylibLocation (clsPtr as Ptr) as CString
    soft declare sub deleteMyClass lib dylibLocation (clsPtr as Ptr)
    
    dim classPtr as Ptr = newMyCPPClass()
    dim  theText as CString = getACStringFromClass(classPtr)
    
    TextArea1.SetString(theText)
    
    deleteMyClass(classPtr)

    XojoTestCPPLibrary.hpp:

    #include <stdio.h>
    
    #ifdef  __cplusplus
    
    class MyCPPClass
    {
    public:
    	MyCPPClass(void) { }
    	~MyCPPClass() { }
    	
    	const char* GetTestString();
    };
    #endif
    
    // C Interface:
    
    typedef void*   HMyCPPClass;
    
    // Need an explicit constructor and destructor.
    extern "C" HMyCPPClass  newMyCPPClass( void );
    extern "C" void			deleteMyClass(HMyCPPClass);
    
    // Each public C method. Takes an opaque reference to the object
    // that was returned from the above constructor plus the methods parameters.
    extern "C" const char * getACStringFromClass(HMyCPPClass);

    XojoTestCPPLibrary.cpp:

    #include "XojoTestCPPLibrary.hpp"
    
    const char* MyCPPClass::GetTestString()
    {
    	return (const char*)"This is a test";
    }
    
    // Functions implemented in a cpp file, declared in header as extern "C" 
    // to give them C linkage and thus are available from a C lib.
    HMyCPPClass newMyCPPClass( void )
    {
    	return reinterpret_cast<void*>(new MyCPPClass());
    }
    
    void deleteMyClass(HMyCPPClass theClassPtr)
    {
    	delete reinterpret_cast<HMyCPPClass*>(theClassPtr);
    }
    
    const char * getACStringFromClass(HMyCPPClass theClassPtr)
    {
    	return reinterpret_cast<MyCPPClass*>(theClassPtr)->GetTestString();
    }

    So that seems as simple as I can make it -- but it's obviously not right. Any guesses?

  18. Christian S

    22 Oct 2017 Pre-Release Testers, Xojo Pro, XDC Speakers, Third Party Store Germany

    What is current error?

    Normally I would expect you need to mark functions to be exported and publicly available.

  19. Christian S

    22 Oct 2017 Pre-Release Testers, Xojo Pro, XDC Speakers, Third Party Store Germany

    Xcode debug build is often 64-bit only nowadays. Your project maybe 32-bit?

  20. Eli O

    is not verified 22 Oct 2017 Europe (Berlin, Germany)

    Code looks ok to me. Have a loot at: Friday Q&A 2009-11-06: Linking and Install Names

  21. Newer ›

or Sign Up to reply!