Safe use of containers in the C ++ library interface

When developing a C ++ library, I read that it is bad practice to include standard libraries such as std::vector in the public interface (see, for example, Consequences of using std :: vector in a dll exported function ).

What if I want to expose a function that accepts or returns a list of objects? I could use a simple array, but then I would have to add the count parameter, which would make the interface more cumbersome and less secure. Also, this would not help if I wanted to use map , for example. I think libraries like Qt define their own containers that are safe for export, but I would prefer not to add Qt as a dependency, and I don't want to roll back my own containers.

What is the best practice for containers in the library interface? Maybe a tiny container implementation (preferably only one or two files that I can access with a permissive license) that I can use as glue? Or there is even a way to make std::vector , etc. Safe across .DLL / .so borders and with different compilers?

+12
c ++ containers api-design
source share
4 answers

In fact, this applies not only to STL containers, but applies to almost all types of C ++ (in particular, also to all other types of standard libraries).

Since ABI is not standardized, you may run into all the troubles. Typically, for each supported version of the compiler, you need to provide separate binaries for them to work. The only way to get a truly portable DLL is to stick with a simple C interface. Usually this results in something like COM , since you have to make sure that all distributions and corresponding deallocations occur in one module and that no details about the actual layout of the object are provided to the user.

+4
source share

You can implement the template function. This has two advantages:

  • It allows your users to decide what types of containers they want to use with your interface.
  • This frees you from having to worry about compatibility with ABI, because there is no code in your library, it will be created when the user calls the function.

For example, put this in your header file:

 template <typename Iterator> void foo(Iterator begin, Iterator end) { for (Iterator it = begin; it != end; ++it) bar(*it); // a function in your library, whose ABI doesn't depend on any container } 

Then your users can invoke foo with any type of container, even the ones they invented that you don't know about.

One drawback is that you will need to set the implementation code, at least for foo.

Edit: You also said you want to return the container. Consider alternatives, such as a callback function, like in the old days of gold in C:

 typedef bool(*Callback)(int value, void* userData); void getElements(Callback cb, void* userData) // implementation in .cpp file, not header { for (int value : internalContainer) if (!cb(value, userData)) break; } 

This is a fairly old "C" training course, but it gives you a stable interface and is pretty convenient for use by almost any subscriber (even the real C code with minor changes). These two errors are void * userData so the user can interfere with some context there (say if they want to call a member function) and the return type is bool to let the return message tell you to stop. You can make the callback a lot more attractive with std :: function or something else, but this can lead to the defeat of some of your other goals.

+3
source share

TL; DR No problem if you distribute either source code or compiled binaries for the various supported collections (implementation of ABI + Standard Library).

In general, the latter is considered cumbersome (with reasons), so the leadership.


I trust manual advice as far as I can throw them ... and I urge you to do the same.

This guide addresses the issue of compatibility with ABI: ABI is a complex set of specifications that defines the exact interface of a compiled library. It includes:

  • layout layout structures
  • function name
  • calling functions
  • exception handling, runtime information, ...
  • ...

See, for example, Itanium ABI for details. Unlike C, which has a very simple ABI, C ++ has a much more complex surface area ... and therefore many different ABIs have been created for it.

In addition to compatibility with ABI, there is also a problem with the implementation of the standard library. Most compilers have their own implementation of the Standard Library, and these implementations are incompatible with each other (for example, they are not the same as std::vector , although they all implement the same interface and guarantees).

As a result, a compiled binary file (executable file or library) can be mixed and compared with another compiled binary code, if both of them were compiled against the same ABI and compatible versions of the standard library implementation.

Greetings: There is no problem if you distribute the source code and compile the client.

+3
source share

If you are using C ++ 11, you can use cppcomponents. https://github.com/jbandela/cppcomponents

This will allow you to use, among other things, std :: vector as a parameter or return value in Dll / .so files created using different compilers or standard libraries. Take a look at my answer to a similar question for an example Passing a reference to an STL vector across a dll border

Note for example, you need to add CPPCOMPONENTS_REGISTER(ImplementFiles) after the CPPCOMPONENTS_DEFINE_FACTORY() statement

0
source share

All Articles