12

I was having a discussion with a colleague today. He claimed that writing a DLL in C would allow for any other application, written in whatever language, to use that DLL. BUT, if that DLL is written in C++, the number of applications that could use that DLL is limited (maybe because of language constraints).

  1. Is he correct in saying that?
  2. If you were to write a DLL that should be used by all sorts of applications written in all sorts of languages (but on the same platform; let's forget portability for a bit), would you write it in C/C++ and why?

I hope this question isn't a Gorilla vs. Shark kinda question. If it is, please close it.

Community
  • 1
  • 1
Anish Ramaswamy
  • 2,326
  • 3
  • 32
  • 63
  • 1
    My OneJoker libraries are written in C, and callable from C, C++, Java, and Python on Linux and Windows. This was reasonably simple, because Java's JNI and Python's ctypes module are both designed to work with C, not C++. C++ drags in a lot of dependencies, mangles the external function names, and so on. Just my experience. – Lee Daniel Crocker May 24 '13 at 08:06
  • 1
    Pretty sure you can `extern "C"` if you need to export a C interface. In relation to second question, you want to look at COM objects (anything can use them and that is the point). – Meirion Hughes May 24 '13 at 08:10

3 Answers3

16

Most languages provide an (easy) way to call C function from a DLL. It is not the case with C++, since C++ ABI (the binary interface of C++ function) is vendor-specific.

Added to that, it will be nearly impossible to interface with C++ DLL that use advanced C++ constructs like templating or the STL.

However, the contents of your DLL can be written in C++, you only need to make sure that your interface is C-compliant. To do that, do not use C++ constructs in you interface and surround your declarations with :

#ifdef __cpluscplus
extern "C" {
#endif

/* You declarations here */

#ifdef __cpluscplus
}
#endif

... this way, you wrap your C++ library with a C interface.

EDIT : As Mats Petersson wrote, do not forget to ensure that you handle every possible C++ exception in your wrapper.

Matthieu Rouget
  • 3,289
  • 18
  • 23
  • Would this C wrapper be considered generally messy? Because unless the underlying C++ code is extremely complicated using STL and templates and such, I have a feeling it would be better to just refactor the code to C. Am I right? – Anish Ramaswamy May 24 '13 at 08:55
  • 2
    No, I do not think so. You underlying code may use advanced C++ concepts , implement algorithms, make use of high level C++ libraries, but its interface can be very simple, like simple start/stop function and functions that take some buffer as parameters and returns some buffers, with data encapsulated in JSON, XML or protobuf data blocks. – Matthieu Rouget May 24 '13 at 08:57
  • 2
    It think it is the very role of encapsulation. Hide advanced processing with a simple interface... However it depends on your library. I you DLL needs to provide *utility* functions and has a very rich API to be exported to the outside world, you may need to rethink about using this DLL in a cross-language manner or try libraries like SWIG (http://www.swig.org/). – Matthieu Rouget May 24 '13 at 09:03
7

1) If the INTERFACE provided by the DLL is indeed a C++ interface, then yes, it makes it hard (if not impossible) for other languages to interface with the DLL.

C++ is more complex to interface with than C, because of the more complex structure of classes (this pointer being passed along, virtual function pointers/VTABLE layout) and exception handling (where the called code has to handle the fact that the exception happened in some way, and to do that, the code needs to be able to "unwind" the call-stack of the code that threw the exception and destroy any objects created on the way until it finds a catch - if that's not present in the call-stack within the DLL you'll get problems - this unwinding is not part of the C++ standard, because the standard does not wish to restrict which architectures and what features a processor needs/should have to implement C++ more than necessary). Catching exceptions within the DLL will solve issues here.

In other words, if the language is not C++ [and possibly from the same vendor] that is calling the code, there is a need for some handling of the C++ to whatever language is calling it. This can get pretty complicated.

Any C++ object need to be translated from/to the relevant language in the calling code. For basic types in common with C, this is generally not a problem, but classes, structs, etc, will have to be matched up against something compatible in the local language.

On the other hand: A C function is very easy to interface with: put the arguments on the stack, call the function, and clean up the arguments on return. Nothing strange happens, no hidden function parameters, no need to unwind stacks. The only slight complication is functions returning a struct (that is bigger than a certain size) - but that's a pretty special case in C. C's "objets" are much simpler, and most languages have types that correspond well to the basic C language types (but C style struct can still cause some interesting problems, and union can be a real challeng if it's used "cleverly").

2) Choosing languages is a complex business, and it largely depends on how the DLL is supposed to be used or what sort of interface it is supposed to provide, and what interfaces it itself should connect to - if your DLL is interfacing to another C++ DLL (or some other C++ code), then you probably want to use C++. But there are ways to produce a C++ DLL that has a C interface, by using extern "C" for the interface functions (and ensuring that nothing throws over the wall to "C", because that will definitely cause problems).

Conclusion: Obviously, by limiting the interface to "only use C++", then there is a further complication in that anyone using the library from, say, C, Python or Lisp [all of which can probably call C functions fairly easily], that user will have to wrap the C++ code in a C language wrapper. And yes, this can be done, and is used quite regularly when some really good library is available in C++, that someone wants to connect to a language that has a C style interface available. It's pretty much the same solution as the "provide a C to C++ interface within the DLL", except it's not provided by the producer of the DLL.

Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
  • Wow I had no idea that exceptions could create such problems! What if the exception is caught inside the DLL function itself? In that case, the caller would not need to do anything right? – Anish Ramaswamy May 24 '13 at 08:53
  • IMO this answer is a bit missleading. Stack unwinding is a responsibillity of the called function, not the caller, as the caller never has any information about what happened inside the function. Passing objects around across languages, doesn't work anyway, because you have to translate the objects between the languages anyway. C deals with less complex objects, so the overhead is lesser, but it still must be done nevertheless. Having a "hidden" parameter in C++ is part of the calling convention. You have to know about this, just like you have to know that names in C get a '_' prepended. – Devolus May 24 '13 at 08:54
  • @Devolus: Ok, I will rephrase it a bit. – Mats Petersson May 24 '13 at 09:01
0

Each language has it's own characeristics like calling convention, stack setup and other stuff. Whenever you try to deal with function calls crossing language boundaries, you have to deal with this. Compilers support usually a variatey of calling conventions, so you have to make sure that the definitions and compilation is correct and then you can use any language to communicate with modules from others. The statement as such, that it is easier or harder to do, from one language or another as such, is not true, because you always have to deal with this at some point.

I select the language that suits me best for my task, not the other way around. When I write GUI apps I usually use Java, because it lets me concentrate on the solution instead of tracking memory. :) When I want performance I use C or C++ or even assembler, so the choice of language depends on what I intend to do with it or how my environment looks.

Devolus
  • 21,661
  • 13
  • 66
  • 113