Open emmt opened 7 months ago
Silly me, I forgot to change the tests too (done in 7b35c05a61bde0540e36afce1c9817723141e824). I should have made 2 different PRs (one for passing structures by address, the other for the alternative API) sorry for that.
Thank you @emmt !
int prima_minimize(const prima_algorithm_t algorithm, const prima_problem_t *problem, const prima_options_t *options, prima_result_t *const result);
Why do we use pointers here for problem
and options
?
Why do we use pointers here for
problem
andoptions
?
Because passing them by value is not necessary and is a waste of stack space. It is possible but rather unusual in C to pass structures by value (except maybe very small ones). The pointers of these are const
so their contents is not supposed to be modified.
Then may I suggest
int prima_minimize(const prima_algorithm_t algorithm, const prima_problem_t *const problem, const prima_options_t *const options, prima_result_t *const result);
as neither the pointer itself nor its content is supposed to be changed in the function body?
Of course, any change to the pointer itself will be lost since itself is passed by value, but this will effectively prevent the developers (ourselves, future ourselves, and others) from making mistakes when composing or modifying the implementation of this function. I had a discussion with @amontoison about it and this was the consensus.
Thank you.
Honestly, the const
qualifier in function prototypes is only useful for arrays and pointers to indicate that the corresponding content is not modified by the called function. Otherwise, it indicates that the value of the argument (not the content at that address if it is an array or a pointer) is not modified which is irrelevant for the caller and may only be relevant for the optimizer. Modern optimizers can infer this themselves and const
is then just a nuisance for the caller when he/she attempt to decode the meaning of the function prototype. To add to the confusion, the const
qualifier can be before or after the type. Hence, to simplify the task of understanding the function prototype for the user, I usually restrict the use of const
to the former case. But I'll adapt to the consensus...
Why do we use pointers here for
problem
andoptions
?Because passing them by value is not necessary and is a waste of stack space. It is possible but rather unusual in C to pass structures by value (except maybe very small ones). The pointers of these are
const
so their contents is not supposed to be modified.
Thanks for the explanation with stack space! I understand now why it's more relevant to pass structures by pointer.
For the const
, we should think of a compromise between preventing he developers from making mistakes but also the readability of the headers.
@emmt is more expert than me on C, we should probably follow his advice.
For the new C API, it looks good to me. 👍
To add to the confusion, the const qualifier can be before or after the type. Hence, to simplify the task of understanding the function prototype for the user, I usually restrict the use of const to the former case.
Then a user like me would need to check a documentation to make sure what the const really means, because, as you said, there are two different cases. If we use both, the user knows automatically that everything is supposed to be constant.
@zaikunzhang Except that when there is a single const
for a pointer the user has to wonder whether it is the value of the pointer that remains constant (which is irrelevant for the user) or the values at the address given by the pointer that are left unchanged (which is of much importance for the caller). If you look at the prototypes of the functions of the C standard library, they follow what I suggest: reserve the const
qualifier to indicate that a pointer argument is for read-only access to the pointed memory (without const
, read-write access has to be assumed). You can check that this is also the case of many well known C library. This usage of the const
qualifier is simpler to interpret and more clear.
This PR implements the alternative C API discussed here and here. The changes come in two commits:
the first commit (0aa20694383602cb69918fa13fa4b311d6669c0b) is a simple modification of the existing C API so that all structures are passed by address (not by value);
the second commit (d90d0a3b5f13c65f50309ea33571596d24e4c14a) implements a draft of the proposed alternative C API with:
prima_context_t
storing all the parameters,prima_context_create
andprima_context_destroy
to create this structure and, when no longer needed, destroy it as well as related resources),prima_context_set_...
and accessorsprima_context_get_...
) to set and retrieve all parameters.All C examples compile and run fine with the first commit. Example are needed for the 2nd commit but it is mainly there for being discussed.
The two king of API can coexist. In fact the latter one is implemented on top of the former one and is intended to facilitate the writing of bindings in other languages without requiring to directly access C structures in these languages.