I would like to interface with a C library I've written from a C++ program. The C library is written using modern C, and has made use of the static
array specifier to show the minimum length of an array, or that a pointer cannot be NULL
.
When I try to write a program that interfaces with an extern "C"
function using this feature I get the following message:
error: static array size is a C99 feature, not permitted in C++
Is it not possible to interface with this C library? Will I have to modify the C library, or is there an alternative available?
Here's an example program that causes the error:
// foo.h
#ifndef FOO_H
#define FOO_H
void foo(int i[static 1]);
#endif //FOO_H
// foo.c
#include <stdio.h>
void foo(int i[static 1]) {
printf("%i", i[0]);
}
// main.cpp
extern "C"
{
void foo(int i[static 1]);
}
int main() {
int i[] = {1};
foo(i);
}
extern "C"
indicates to the C++ compiler that the function name should not be mangled. Since you're linking against an external library, the expectation is that the external library has a function (and only one function) called foo
. The static
keyword in C99 and onward in an array size tells the compiler that "this array will be at least this size", which may allow the compiler to make certain optimizations (I don't know what optimizations these could be, but consider that it could possibly do loop unrolling up to N = 4
, where you declared void foo(int i[static 5]);
If you pass an array that is not at LEAST this size, you could have a bad time.
The immediate solution is just that we need to tell the C++ compiler:
foo
int *
as a parameterextern "C"
{
void foo(int i[]);
}
But we lose the information to anyone using this in the C++ program that this function MUST be at least size N (which is what the static
keyword in the array size meant). I can't think of a good way to force a compile-time check on this except possibly through some type templated wrapper function:
#include <cstddef>
extern "C"
{
void foo(int i[]);
}
template <std::size_t N>
void c_foo(int i[N])
{
static_assert(N >= 5);
foo(i);
}
int main(int argc, char** argv)
{
int a[5] = {1, 2, 3, 4, 5};
int b[4] = {1, 2, 3, 4};
c_foo<5>(a); // this will be fine
c_foo<4>(b); // this will raise a compile-time error
}
To be extra safe, I'd put the function prototypes for your c_foo
functions and any "safe" extern "C"
prototypes in one c_library_interface.h
file, and the function definitions for your c_foo
functions and any "unsafe" extern "C"
prototypes in another c_library_interface_unsafe.cpp
file. That way, as long as you don't include the unsafe file in your main C++ files, you should only be able to interface with the static
array size functions through the templates, which will do some size checking.
(This is additional information to the answer by John)
The C header is not correct in C++ so you will have to modify it .
Probably the intent of [static 1]
is to indicate that the function should not be called with a null pointer. There's no standard way to indicate this in both languages and the author's choice is not compatible with C++.
Some major compilers support __attribute__((nonnull))
in both languages, either as a postfix to each parameter, or as a prefix to the function which then applies to all pointer parameters.
In my personalized header I define a preprocessor macro that expands to the equivalent syntax for each compiler , or blank for compilers that don't support it.
Bear in mind that there's no requirement for a compiler to enforce the behaviour and there will certainly be cases where it doesn't (e.g. passing on a received pointer that it doesn't know anything about).
So IMHO with the current state of compiler attitudes towards this feature (be it the attribute or the static 1
) , this should be viewed as a form of user documentation.
I actually have decided not to use it in my own code, after some experimentation: using this attribute will cause the compiler to optimize out any null-pointer checks in the function body, which introduces the possibility of runtime errors since there is no effective prevention of null pointers being passed. To make the feature usable, the compiler would also have to issue diagnostics any time the function is called and the compiler cannot guarantee the argument is non-null. (Which is an option I would like to see in compilers, but so far as I know, doesn't exist yet).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With