6

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);
}
15
  • You can't magically make a C++ Compiler understand VLAs. extern "C" only specifies a language binding, not magically teaches a C++ Compiler the language referred to. Commented Mar 3, 2020 at 23:57
  • 1
    Correct me if I'm wrong, but the function signature in C is just an unmangled name. extern C means, "Hey, don't mangle this name. This is the function signature as is". You shouldn't be actually DEFINING the function in the extern "C" block, right? You're just supposed to link to it? So you'd write extern "C" { void foo(int i[]); } and make sure you link with the library. I don't do a lot (or any at all) interfacing with C libraries. I've just seen it in the past. Commented Mar 4, 2020 at 0:06
  • 1
    Same error? My suggestion OMITS the static keyword though. You shouldn't get that error at all. Commented Mar 4, 2020 at 0:21
  • 1
    I'll write up an answer. There are some caveats to this. For one, the C compiler that created the library may have made some optimizations to the code EXPECTING that all arrays passed in are at least the given size. For example, possibly loop unrolling up to size N? Who knows. If the compiled code requires the array to be that size, and you pass something not large enough, that type safety is busted. So you have to be really careful when calling the function. I'm not sure how to ensure that safety. Commented Mar 4, 2020 at 0:25
  • 1
    @John although in the case of static 1 specifically, it's redundant as Standard C and C++ do not support zero-sized arrays anyway. Perhaps this is supposed to indicate "pointer should not be a null pointer" Commented Mar 4, 2020 at 0:38

2 Answers 2

7

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:

  1. There is a function called foo
  2. It takes an int * as a parameter
extern "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.

Sign up to request clarification or add additional context in comments.

1 Comment

From what I know, compilers don't actually implement [ static n] any different from ordinary pointers and this C99 feature was rather misguided. In order to actually increase type safety at compile-time, the compiler would need to see the function declaration, the function call and a static size array, all inside the same translation unit.
2

(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).

3 Comments

"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." This is my take too, I have yet to see a compiler generating any different code upon spotting [static n].
@Lundin i've seen gcc remove null pointer checks for __attribute__((nonnull)) , haven't checked for [static 1] but it seems reasonable to assume they might do it in future if they don't already
Doesn't seem to be the case currently: godbolt.org/z/w2fQah. The attribute nonnull works, static doesn't. However, clang gives identical machine code for both examples. So it seems that at least clang does implement this.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.