C Bindings
The C bindings are distributed as a zip file with the following directory layout:
- single header file in
/include/dnp3.h - cmake find package script in
/cmake/ - platform specific shared libraries in
/lib/<platform> - 3rd party license information in
/lib/licenses.txt
CMake Usage#
Make the find package script discoverable by adding it to the prefix path. Then call find_package:
Then declare a dependency on the package:
Mapping#
Many of the concepts built into higher-level languages are simply design patterns or idioms in C. When you see these higher levels patterns discussed in this guide, you can use the idioms here to understand how they map to C.
Classes#
C doesn't have classes with restricted member visibility. Instead, you can use opaque types to hide implementation details:
You can then define constructors and destructors as functions:
Class "member functions" are simply functions that take a pointer to the opaque type as the first argument:
Interfaces#
Function polymorphism in C is accomplished using function pointers. Interfaces are simply collections of functions pointers along with some optional context. For example, consider the following logging "interface" in the library:
ctxis an opaque pointer to some state information required by the interface. It is passed into every method as the final argument.on_destroyis the destructor that cleans up thectxon_messageis a function pointer used to dispatch a log message.
This interface only contains a single method, but other interfaces contain a number of methods.
tip
If your implementation of an interface is stateless, you can initialize ctx and on_destroy to NULL. C99 struct initialization syntax
will do this by default if you don't specify a value for these fields.
Iterators#
Collections in the C bindings are always implemented as an opaque iterator type. You can think of them as a class with a single "next" method. Consider an iterator over bool values:
If you are given this iterator in a callback you can process the values in a loop:
warning
You should never use an iterator outside of the callback. Frequently, the iterator points to memory on the call stack and will result in undefined behavior if used after the callback has completed.
Error Handling#
Error handling in the C API is performed using error codes. An error code is always an enum value with the first value equal to zero and indicating success.
Consider an error enum and a function that parses a string as an int that can fail:
When a function that can fail needs to return a value, it always does so using an out parameter as the last argument. User code should always check returned error values:
note
Every enum generated by the C code generator also has a generated *_to_string helper method to aid in debugging.