1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
/* C++ shim for the CLP class-only knobs (dual-steepest-edge pricing,
* factorization frequency, hot-start snapshot/restore).
*
* This file is compiled as C++17 and exposes five functions with C linkage. It
* exists as a separate translation unit because the main wrapper
* (clp_wrapper.c) is compiled as plain C and reaches CLP only through
* <Clp_C_Interface.h>, which does NOT expose markHotStart/solveFromHotStart/
* unmarkHotStart, setFactorizationFrequency, or the dual-row pivot setter.
* Those live solely on the C++ ClpSimplex class.
*
* Handle layout (critical): the opaque handle threaded through the FFI is the
* pointer cobre_clp_create() returns from Clp_newModel(). That is NOT a
* ClpSimplex* — it is a `Clp_Simplex*` WRAPPER struct. With CLP_EXTERN_C
* defined, Coin_C_defines.h declares Clp_Simplex as a concrete
* `{ ClpSimplex* model_; CMessageHandler* handler_; }`, and Clp_newModel()
* does `model->model_ = new ClpSimplex()` (Clp_C_Interface.cpp). So the real
* C++ ClpSimplex object lives at `wrapper->model_`. Every Clp_* C-API call in
* clp_wrapper.c reaches it the same way (the C API dereferences ->model_
* internally). This shim MUST do the same: cast the void* to Clp_Simplex* and
* take ->model_. Casting the wrapper directly to ClpSimplex* (an earlier bug)
* reinterprets the wrapper's two leading pointers (model_/handler_) as
* ClpSimplex members and dispatches every method on garbage.
*
* We define CLP_EXTERN_C before including the CLP headers so the Clp_Simplex
* struct is the concrete two-pointer form (not `typedef void`); that is what
* makes ->model_ accessible. CLP_EXTERN_C only affects that typedef and gives
* the C API declarations C linkage; this shim calls no Clp_* C function, only
* ClpSimplex class methods, so it has no other effect here.
*
* The hot-start saveStuff token is allocated and owned by CLP. The Rust side
* keeps it opaque (never dereferences it) and pairs every mark with an unmark
* on the same model instance, so the token's lifetime is bounded by the
* persistent solver instance that produced it.
*/
// With CLP_EXTERN_C defined, <Coin_C_defines.h> (pulled in by
// <Clp_C_Interface.h>) declares Clp_Simplex as a concrete struct whose first
// member is `ClpSimplex *model_;`. That struct references the C++ classes
// ClpSimplex and ClpSolve by name, so their class headers MUST be included
// FIRST — otherwise Coin_C_defines.h sees undeclared types and the struct
// silently degrades. Order is therefore: define the macro, include the C++
// class headers, then the C interface header.
extern "C" /* extern "C" */