osqp / qdldl

A free LDL factorisation routine

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

interop issues with QDLDL_bool and C++

mlubin opened this issue · comments

I know I'm responsible for suggesting this, but I realized there's a design issue with

// Use bool for logical type if available
#if __STDC_VERSION__ >= 199901L
typedef _Bool QDLDL_bool;
#else
typedef int   QDLDL_bool;
#endif

in qdldl_types.h. If you include qdldl.h from C++, the second case will be hit regardless of how qdldl was compiled. I think the logic behind this compiler check is flawed, because it's impossible to know at header include time which C compiler was used for qdlql. I did a bit of searching and it seems like there's no good solution for an interoperable boolean type between C and C++. Fortunately QDLDL_bool does very little in the header, it's just used for allocating a work vector. It would be safe to move this C99 check into the source file and use another type in the header. I also tried

typedef char QDLDL_bool;

and this seems to work fine and accomplish the goal of a 1-byte type that can store 0/1 values.

I like the char option. It seems the easiest solution. What do you think @goulart-paul ?

The only purpose of the above is to ensure that the QDLDL_bool we define will make sense to the C compiler, which will not be the case if we have a C89 only compiler and we ask for _Bool.

It might be better to check for compiler support of '_Bool' in cmake directly before building qdldl, using something like https://cmake.org/cmake/help/v3.0/module/CheckCSourceCompiles.html

The process in cmake could be:

  1. Include a small check in cmake to see if test code with a _Bool compiles.

  2. Define QDLDL_BOOL_TYPE accordingly within cmake, taking it as either int or _Bool depending on the outcome of step 1.

  3. Put typedef @QDLDL_BOOL_TYPE@ QDLDL_bool; into qdldl_types.h.in as a single line instead of switching on __STDC_VERSION__ as above.

In this way the header would be unambiguous about the logical type we use and we would be sure that the header is consistent with what we actually built.

If the above seems like it would fix the problem with C++ compatibility I can try it.

From what I can tell, _Bool will not work for C++. It's not defined in the C++ standard or supported by the C++ compilers that I tried. I also tried using bool and including stdbool.h but that didn't work either on the C++ side.

Another option would be to just make it an enum, e.g.

typedef enum _QDLDL_bool { false, true } QDLDL_bool;

That ends up as the same size as int I think, which is not quite as compact as it could be but better than nothing. The only point of any of this was to avoid it being long long when we define QDLDL_int that way.

enums are also a bit problematic to use at library boundaries because their size is not defined in the standard. If you compile qdldl into a shared library with compiler 1 and try to link to it with compiler 2, you have a similar issue.

Do you have an objection to char?

Not really, no. It was unclear to me when writing this whether forcing the type to be 8 bits exactly might be slower than allowing it to be whatever is the most efficient size for the platform (i.e. I would have preferred int_fast8_t, but that is also C99 only). I can't say for sure if that's anything beyond a hypothetical concern though.

That's also hard to test since I don't know on which platforms int_fast8_t is strictly larger than char.

If char is too awkward as a bool store, int is also reasonable. You can also ask for an int* for the bool storage in the header and cast it to something else. You have to be careful about memory alignment issues if you're casting it to a pointer to a type of a different size than int, however.

I have proposed a fix in #12 that changes the definition of the bool type to unsigned char.

fixed in #12