What's the easiest way to consume a C++ library in Delphi. In the past I've create a separate C API layer as a DLL that I can call from Delphi. For a large C++ library it is very time consuming to create that additional layer. Wanted to ask if anyone has any ideas before I embark on creating a C API.

What's the easiest way to consume a C++ library in Delphi. In the past I've create a separate C API layer as a DLL that I can call from Delphi. For a large C++ library it is very time consuming to create that additional layer. Wanted to ask if anyone has any ideas before I embark on creating a C API.

PS The solution needs to be a mechanism that is crossplatform, so I can't use COM.

Comments

  1. If you can't use com then you've already found the solution. Your wrapped C API.

    ReplyDelete
  2. I don't suppose there is a magical tool that can generate the C API automatically?

    ReplyDelete
  3. You could try building the C++ library in C++Builder, then making a Delphi class / interface that's how you want to interact with it. On the C++ side, then implement that class calling into or wrapping your library.

    From the Delphi side, Delphi thinks it's using a Delphi class, but really it's C++. This also lets you use Delphi types like string and convert them on the C++ side to whatever your C++ library needs. For a larger library, this will be as time-consuming as a C layer, but you may find it more rewarding in terms of code quality, and you could also consider wrapping only the parts you specifically want to use.

    ReplyDelete
  4. C++ builder is worth looking at. I'll try it first on a simple example.

    ReplyDelete
  5. One question, is the memory layout of a c++ builder object the same as an object in Delphi? Or is that too dangerous to rely on?

    Could I, for example, create an abstract class in Delphi which has the same list of c++ methods in the c++ object, add a C method to the c++ code that creates and returns a pointer to the c++ object at which point I cast the returned pointer to the Delphi abstract class and thereby have access to the c++ methods? If the layout is the same it should work?

    I can see problems if the c++ methods use c++ strings and other incompatible types. Though I could write some helper C exported methods that can convert strings to and from simple C char arrays.

    At the moment I'm leading towards writing a flattened C API (which I've done before, it's just time consuming) as I can be selective in what I want exported since I probably don't need every class and every method exposed. But I'm curious about the possibility of using c++ builder.

    ReplyDelete
  6. I doubt that there's a tool to perform this magic. As for using C++ Builder I'd be wary about taking a dependency on another proprietary tool. A flattened C interface is no more work.

    ReplyDelete
  7. I know that mpv@mORMot-forum went the same route successfully implemented a Delphi wrapper for two major versions of the SpiderMonkey JavaScript Engine.

    ReplyDelete
  8. Herbert Sauro You can go these way, it will work if you only use simple types in the interface. We are using these a lot between VS and Delphi, but you must be very carefull with the "ownership" of these classes. And we declare it always with stdcall like:
    Tvsclass = class
    public
    function vsfunc(i : integer): integer; virtual; stdcall; abstract;
    end

    ReplyDelete
  9. The object layout is the same on the C++ and Delphi side when it's a 'Delphi-style class'. C++ classes can be very minimal, so a plain old class C {} is not the same as a __declspec(delphiclass) class C : public TObject {}, both in layout and in Delphi-ness (TObject inheritance, RTTI, etc.)

    I made a video showing how to mix C++ and Delphi and I show the technique I mention above, where I have a Delphi abstract class and implement it in C++, but call it from Delphi. There's even source code :) The github page has a link to a blog post and to a video. github.com - CodeRage2016

    ReplyDelete
  10. Herbert Sauro This is called Interface. And they are cross-platform. You do not need full-blown COM to use them inside your app.

    ReplyDelete
  11. Swig for Delphi! Will take a look, thanks,

    ReplyDelete
  12. As said previously, you don't need COM-Services and the like for interfaces. Just make a C++ class implement one, return it as such into delphi and there you go.

    ReplyDelete
  13. Alexander Benikowski That's a COM interface isn't it, reliant on the binary interop of a standard vmt

    ReplyDelete
  14. David Heffernan Jup. But C++ can do that and therefore it is crossplatform.

    ReplyDelete
  15. Alexander Benikowski How is it cross platform. On Windows what you propose relies on the COM binary interop standard. Which all mainstream C++ compilers support. But what about other platforms. I see no particular reason for the same interop to be valid.

    ReplyDelete
  16. David Heffernan In C++ you simply declare virtual only classes defining the methods with appropiate calling conventions and parameter types. Another C++Class implements this. Done
    You can now use this for any delphi Platform.

    ReplyDelete
  17. Alexander Benikowski A priori that is not correct. That only works on Windows because of the binary compatibility enforced by the need to support COM. Nothing in any C++ standard mandates how a class of abstract virtual methods is to be implemented. Compilers are free to make their own choices.

    ReplyDelete
  18. David Heffernan okay seems you're right. Nontheless, i'd check if the most common ones(like gcc, clang) actually do have a different binary layout on different platforms. I doupt that.

    ReplyDelete
  19. Alexander Benikowski They may well do. My point is that you need to check, and can't take it for granted, and think about the implications of committing to such a route and then finding out that a compiler you want to support is not compatible.

    ReplyDelete
  20. David Heffernan I belive, that compiler in C++ Builder actually creates Delphi compatible interfaces on all platforms.

    ReplyDelete
  21. +Микола Петрівський I expect that is the case. It would be quite a constraint though to be forced to use that compiler.

    ReplyDelete
  22. Alexander Benikowski David Heffernan Just making a pure virtual C++ class won't give you a class layout compatible with COM, or even with C++ code generated by another compiler. All compilers are free by the C++ spec to do their own thing.

    A COM-compatible layout will come when you use a Delphi-style class and interfaces, because both languages' compilers have baked-in COM support. So COM is certainly a possibility here. Whichever route you choose, though (flat C wrapper, COM, Delphi class implemented by C++) you will need to write that intermediate layer.

    ReplyDelete
  23. David Millington That cannot be right. That would mean that standard Windows C++ code for COM would not work under C++ Builder. And I don't believe that is the case. I think you should branch out and look at how COM is accessed from non Embarcadero compilers. Then I think you'll understand me.

    ReplyDelete
  24. David Heffernan We might be talking about two different things, but if you mean that there is no specified ABI or class layout for C++ classes, it's true - there isn't.

    COM comes with certain contracts, but you need a compiler that speaks COM. You can implement COM with anything, even with plain C (horrific though that would be, I know it was done regularly in the nineties.)

    ReplyDelete
  25. David Millington The reality on the ground is that all viable C++ compilers on Windows implement a class layout that is compatible with COM. Otherwise they would not be able to support COM. For example, this is from the platform SDK:

    IPersistFile : public IPersist
    {
    public:
    virtual HRESULT STDMETHODCALLTYPE IsDirty( void) = 0;

    virtual HRESULT STDMETHODCALLTYPE Load(
    /* [in] / __RPC__in LPCOLESTR pszFileName,
    / [in] / DWORD dwMode) = 0;

    virtual HRESULT STDMETHODCALLTYPE Save(
    / [unique][in] / __RPC__in_opt LPCOLESTR pszFileName,
    / [in] / BOOL fRemember) = 0;

    virtual HRESULT STDMETHODCALLTYPE SaveCompleted(
    / [unique][in] / __RPC__in_opt LPCOLESTR pszFileName) = 0;

    virtual HRESULT STDMETHODCALLTYPE GetCurFile(
    / [out] */ __RPC__deref_out_opt LPOLESTR *ppszFileName) = 0;

    };

    Every Windows compiler that wants to be able to support COM is pretty much forced to layout that class in the same way, using a vmt etc.

    There is no C++ ABI. But on Windows, the existence of COM, means that there is a defacto ABI for pure virtual classes.

    ReplyDelete
  26. David Heffernan I think we're both agreeing with each other :)

    ReplyDelete
  27. Well, maybe now. But I wasn't agreeing with you when you said:

    A COM-compatible layout will come when you use a Delphi-style class and interfaces, because both languages' compilers have baked-in COM support.

    ReplyDelete

Post a Comment