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

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

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.

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.

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

@Dave S –

[quote]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.
[/quote]

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

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:

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.

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

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

@Eli Ott –

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.

@Christian Schmitz – 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.

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.

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.

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.

[quote=354272:@Stephen Greenfield]@Eli Ott –
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.[/quote]
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.

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

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.

@Eli Ott –

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

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.

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.

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:

[code]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)
[/code]

XojoTestCPPLibrary.hpp:

[code]#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);[/code]

XojoTestCPPLibrary.cpp:

[code]#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();
}[/code]

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

What is current error?

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

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

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