Skip to content

Commit ff7b613

Browse files
committed
Polish macro of dynamic loading and support Gurobi 12.0.0
1 parent 1eda0a0 commit ff7b613

File tree

13 files changed

+182
-109
lines changed

13 files changed

+182
-109
lines changed

include/pyoptinterface/copt_model.hpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "pyoptinterface/core.hpp"
88
#include "pyoptinterface/container.hpp"
99
#include "pyoptinterface/solver_common.hpp"
10+
#include "pyoptinterface/dylib.hpp"
1011

1112
extern "C"
1213
{
@@ -87,10 +88,8 @@ extern "C"
8788

8889
namespace copt
8990
{
90-
#define B(f) extern decltype(&::f) f
91-
91+
#define B DYLIB_EXTERN_DECLARE
9292
APILIST
93-
9493
#undef B
9594

9695
bool is_library_loaded();

include/pyoptinterface/dylib.hpp

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,4 +63,29 @@ class DynamicLibrary
6363

6464
private:
6565
void *handle = nullptr;
66-
};
66+
};
67+
68+
// Next we will introduce some magic macros to declare function pointers and load them from dynamic
69+
// library robustly
70+
71+
#define DYLIB_EXTERN_DECLARE(f) extern decltype(&::f) f
72+
#define DYLIB_DECLARE(f) decltype(&::f) f = nullptr
73+
74+
#define DYLIB_LOAD_INIT \
75+
Hashmap<std::string, void *> _function_pointers; \
76+
bool _load_success = true
77+
78+
#define DYLIB_LOAD_FUNCTION(f) \
79+
{ \
80+
auto ptr = reinterpret_cast<decltype(f)>(lib.get_symbol(#f)); \
81+
if (ptr == nullptr) \
82+
{ \
83+
fmt::print("function {} is not loaded correctly\n", #f); \
84+
_load_success = false; \
85+
} \
86+
_function_pointers[#f] = ptr; \
87+
}
88+
89+
#define IS_DYLIB_LOAD_SUCCESS _load_success
90+
91+
#define DYLIB_SAVE_FUNCTION(f) f = reinterpret_cast<decltype(f)>(_function_pointers[#f])

include/pyoptinterface/gurobi_model.hpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "pyoptinterface/core.hpp"
88
#include "pyoptinterface/container.hpp"
99
#include "pyoptinterface/solver_common.hpp"
10+
#include "pyoptinterface/dylib.hpp"
1011

1112
// define Gurobi C APIs
1213
#define APILIST \
@@ -69,10 +70,8 @@
6970

7071
namespace gurobi
7172
{
72-
#define B(f) extern decltype(&::f) f
73-
73+
#define B DYLIB_EXTERN_DECLARE
7474
APILIST
75-
7675
#undef B
7776

7877
bool is_library_loaded();

include/pyoptinterface/highs_model.hpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "pyoptinterface/core.hpp"
99
#include "pyoptinterface/container.hpp"
1010
#include "pyoptinterface/solver_common.hpp"
11+
#include "pyoptinterface/dylib.hpp"
1112

1213
#define APILIST \
1314
B(Highs_create); \
@@ -62,10 +63,8 @@
6263

6364
namespace highs
6465
{
65-
#define B(f) extern decltype(&::f) f
66-
66+
#define B DYLIB_EXTERN_DECLARE
6767
APILIST
68-
6968
#undef B
7069

7170
bool is_library_loaded();

include/pyoptinterface/ipopt_model.hpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include "solvers/ipopt/IpStdCInterface.h"
44
#include "pyoptinterface/nleval.hpp"
5+
#include "pyoptinterface/dylib.hpp"
56
#include <cmath>
67

78
#define APILIST \
@@ -14,10 +15,8 @@
1415

1516
namespace ipopt
1617
{
17-
#define B(f) extern decltype(&::f) f
18-
18+
#define B DYLIB_EXTERN_DECLARE
1919
APILIST
20-
2120
#undef B
2221

2322
bool is_library_loaded();

include/pyoptinterface/mosek_model.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "pyoptinterface/core.hpp"
1212
#include "pyoptinterface/container.hpp"
1313
#include "pyoptinterface/solver_common.hpp"
14+
#include "pyoptinterface/dylib.hpp"
1415

1516
#define APILIST \
1617
B(MSK_getcodedesc); \

lib/copt_model.cpp

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
1-
#include "pyoptinterface/dylib.hpp"
2-
#include "pyoptinterface/solver_common.hpp"
31
#include "pyoptinterface/copt_model.hpp"
42
#include "fmt/core.h"
53

64
namespace copt
75
{
8-
#define B(f) decltype(&::f) f = nullptr
9-
6+
#define B DYLIB_DECLARE
107
APILIST
11-
128
#undef B
139

1410
static DynamicLibrary lib;
@@ -27,22 +23,24 @@ bool load_library(const std::string &path)
2723
return false;
2824
}
2925

30-
#define B(f) \
31-
{ \
32-
auto ptr = reinterpret_cast<decltype(f)>(lib.get_symbol(#f)); \
33-
if (ptr == nullptr) \
34-
{ \
35-
fmt::print("function {} is not loaded correctly", #f); \
36-
return false; \
37-
} \
38-
f = ptr; \
39-
}
26+
DYLIB_LOAD_INIT;
27+
28+
#define B DYLIB_LOAD_FUNCTION
4029
APILIST
4130
#undef B
4231

43-
is_loaded = true;
44-
45-
return true;
32+
if (IS_DYLIB_LOAD_SUCCESS)
33+
{
34+
#define B DYLIB_SAVE_FUNCTION
35+
APILIST
36+
#undef B
37+
is_loaded = true;
38+
return true;
39+
}
40+
else
41+
{
42+
return false;
43+
}
4644
}
4745
} // namespace copt
4846

lib/gurobi_model.cpp

Lines changed: 75 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,25 @@
1-
#include "pyoptinterface/dylib.hpp"
2-
#include "pyoptinterface/solver_common.hpp"
31
#include "pyoptinterface/gurobi_model.hpp"
4-
#include <fmt/core.h>
2+
#include "fmt/core.h"
53

6-
namespace gurobi
4+
extern "C"
75
{
8-
#define B(f) decltype(&::f) f = nullptr
6+
int __stdcall GRBloadenvinternal(GRBenv **envP, const char *logfilename, int major, int minor,
7+
int tech);
8+
int __stdcall GRBemptyenvinternal(GRBenv **envP, int major, int minor, int tech);
9+
int __stdcall GRBloadenv_1200(GRBenv **envP, const char *logfile);
10+
int __stdcall GRBemptyenv_1200(GRBenv **envP);
11+
}
912

13+
namespace gurobi
14+
{
15+
#define B DYLIB_DECLARE
1016
APILIST
11-
1217
#undef B
1318

19+
// Gurobi 12.0.0 workarounds
20+
DYLIB_DECLARE(GRBloadenvinternal);
21+
DYLIB_DECLARE(GRBemptyenvinternal);
22+
1423
static DynamicLibrary lib;
1524
static bool is_loaded = false;
1625

@@ -27,25 +36,73 @@ bool load_library(const std::string &path)
2736
return false;
2837
}
2938

30-
#define B(f) \
31-
{ \
32-
auto ptr = reinterpret_cast<decltype(f)>(lib.get_symbol(#f)); \
33-
if (ptr == nullptr) \
34-
{ \
35-
fmt::print("function {} is not loaded correctly", #f); \
36-
return false; \
37-
} \
38-
f = ptr; \
39-
}
39+
DYLIB_LOAD_INIT;
40+
41+
#define B DYLIB_LOAD_FUNCTION
4042
APILIST
4143
#undef B
4244

43-
is_loaded = true;
45+
// We have to deal with Gurobi 12.0.0 where some functions are absent from the dynamic library
46+
{
47+
int major, minor, techinical;
48+
auto GRBversion_p =
49+
reinterpret_cast<decltype(GRBversion)>(_function_pointers["GRBversion"]);
50+
if (GRBversion_p != nullptr)
51+
{
52+
GRBversion_p(&major, &minor, &techinical);
53+
54+
if (major == 12 && minor == 0 && techinical == 0)
55+
{
56+
DYLIB_LOAD_FUNCTION(GRBloadenvinternal);
57+
DYLIB_LOAD_FUNCTION(GRBemptyenvinternal);
58+
_function_pointers["GRBloadenv"] = &GRBloadenv_1200;
59+
_function_pointers["GRBemptyenv"] = &GRBemptyenv_1200;
60+
61+
// Now check there is no nullptr in _function_pointers
62+
_load_success = true;
63+
for (auto &pair : _function_pointers)
64+
{
65+
if (pair.second == nullptr)
66+
{
67+
fmt::print("function {} is not loaded correctly\n", pair.first);
68+
_load_success = false;
69+
}
70+
}
71+
72+
if (_load_success)
73+
{
74+
DYLIB_SAVE_FUNCTION(GRBloadenvinternal);
75+
DYLIB_SAVE_FUNCTION(GRBemptyenvinternal);
76+
}
77+
}
78+
}
79+
}
4480

45-
return true;
81+
if (IS_DYLIB_LOAD_SUCCESS)
82+
{
83+
#define B DYLIB_SAVE_FUNCTION
84+
APILIST
85+
#undef B
86+
is_loaded = true;
87+
return true;
88+
}
89+
else
90+
{
91+
return false;
92+
}
4693
}
4794
} // namespace gurobi
4895

96+
int GRBloadenv_1200(GRBenv **envP, const char *logfile)
97+
{
98+
return gurobi::GRBloadenvinternal(envP, logfile, 12, 0, 0);
99+
}
100+
101+
int GRBemptyenv_1200(GRBenv **envP)
102+
{
103+
return gurobi::GRBemptyenvinternal(envP, 12, 0, 0);
104+
}
105+
49106
static char gurobi_con_sense(ConstraintSense sense)
50107
{
51108
switch (sense)

lib/highs_model.cpp

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
1-
#include "pyoptinterface/dylib.hpp"
2-
#include "pyoptinterface/solver_common.hpp"
31
#include "pyoptinterface/highs_model.hpp"
42
#include "fmt/core.h"
53

64
namespace highs
75
{
8-
#define B(f) decltype(&::f) f = nullptr
9-
6+
#define B DYLIB_DECLARE
107
APILIST
11-
128
#undef B
139

1410
static DynamicLibrary lib;
@@ -27,22 +23,24 @@ bool load_library(const std::string &path)
2723
return false;
2824
}
2925

30-
#define B(f) \
31-
{ \
32-
auto ptr = reinterpret_cast<decltype(f)>(lib.get_symbol(#f)); \
33-
if (ptr == nullptr) \
34-
{ \
35-
fmt::print("function {} is not loaded correctly", #f); \
36-
return false; \
37-
} \
38-
f = ptr; \
39-
}
26+
DYLIB_LOAD_INIT;
27+
28+
#define B DYLIB_LOAD_FUNCTION
4029
APILIST
4130
#undef B
4231

43-
is_loaded = true;
44-
45-
return true;
32+
if (IS_DYLIB_LOAD_SUCCESS)
33+
{
34+
#define B DYLIB_SAVE_FUNCTION
35+
APILIST
36+
#undef B
37+
is_loaded = true;
38+
return true;
39+
}
40+
else
41+
{
42+
return false;
43+
}
4644
}
4745
} // namespace highs
4846

lib/ipopt_model.cpp

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,8 @@ static bool is_name_empty(const char *name)
1313

1414
namespace ipopt
1515
{
16-
#define B(f) decltype(&::f) f = nullptr
17-
16+
#define B DYLIB_DECLARE
1817
APILIST
19-
2018
#undef B
2119

2220
static DynamicLibrary lib;
@@ -35,22 +33,24 @@ bool load_library(const std::string &path)
3533
return false;
3634
}
3735

38-
#define B(f) \
39-
{ \
40-
auto ptr = reinterpret_cast<decltype(f)>(lib.get_symbol(#f)); \
41-
if (ptr == nullptr) \
42-
{ \
43-
fmt::print("function {} is not loaded correctly", #f); \
44-
return false; \
45-
} \
46-
f = ptr; \
47-
}
36+
DYLIB_LOAD_INIT;
37+
38+
#define B DYLIB_LOAD_FUNCTION
4839
APILIST
4940
#undef B
5041

51-
is_loaded = true;
52-
53-
return true;
42+
if (IS_DYLIB_LOAD_SUCCESS)
43+
{
44+
#define B DYLIB_SAVE_FUNCTION
45+
APILIST
46+
#undef B
47+
is_loaded = true;
48+
return true;
49+
}
50+
else
51+
{
52+
return false;
53+
}
5454
}
5555
} // namespace ipopt
5656

0 commit comments

Comments
 (0)