55#include < cstdint>
66#include < cstdlib>
77#include < cstring>
8+ #include < functional>
89#include < map>
910#include < memory>
1011#include < optional>
@@ -47,6 +48,8 @@ template <typename T, typename SFINAE = void> struct Encoder;
4748
4849namespace __private__ {
4950std::vector<ErlNifFunc> &get_erl_nif_funcs ();
51+ decltype (auto ) get_erl_nif_load_callback();
52+ decltype (auto ) get_erl_nif_unload_callback();
5053void init_atoms (ErlNifEnv *env);
5154bool init_resources (ErlNifEnv *env);
5255} // namespace __private__
@@ -1187,6 +1190,12 @@ template <typename T> void raise(ErlNifEnv *env, const T &value) {
11871190// Mechanism for accumulating information via static object definitions.
11881191class Registration {
11891192public:
1193+ // A function compatible with the load callback of Erlang's NIFs.
1194+ using LoadCallback = std::function<void (ErlNifEnv *, void **, fine::Term)>;
1195+
1196+ // A function compatible with the unload callback of Erlang's NIFs.
1197+ using UnloadCallback = std::function<void (ErlNifEnv *, void *)>;
1198+
11901199 template <typename T>
11911200 static Registration register_resource (const char *name) {
11921201 Registration::resources.push_back ({&fine::ResourcePtr<T>::resource_type,
@@ -1200,6 +1209,26 @@ class Registration {
12001209 return {};
12011210 }
12021211
1212+ // Registers a load callback.
1213+ static LoadCallback register_load (LoadCallback callback) {
1214+ if (erl_nif_load_callback) {
1215+ throw std::logic_error (" load callback already registered" );
1216+ }
1217+
1218+ return Registration::erl_nif_load_callback = callback;
1219+ return callback;
1220+ }
1221+
1222+ // Registers an unload callback.
1223+ static UnloadCallback register_unload (UnloadCallback callback) {
1224+ if (erl_nif_unload_callback) {
1225+ throw std::logic_error (" unload callback already registered" );
1226+ }
1227+
1228+ Registration::erl_nif_unload_callback = callback;
1229+ return callback;
1230+ }
1231+
12031232private:
12041233 static bool init_resources (ErlNifEnv *env) {
12051234 for (const auto &[resource_type_ptr, name, dtor] :
@@ -1220,6 +1249,8 @@ class Registration {
12201249 }
12211250
12221251 friend std::vector<ErlNifFunc> &__private__::get_erl_nif_funcs ();
1252+ friend decltype (auto ) __private__::get_erl_nif_load_callback();
1253+ friend decltype (auto ) __private__::get_erl_nif_unload_callback();
12231254
12241255 friend bool __private__::init_resources (ErlNifEnv *env);
12251256
@@ -1228,6 +1259,8 @@ class Registration {
12281259 resources = {};
12291260
12301261 inline static std::vector<ErlNifFunc> erl_nif_funcs = {};
1262+ inline static LoadCallback erl_nif_load_callback = {};
1263+ inline static UnloadCallback erl_nif_unload_callback = {};
12311264};
12321265
12331266// NIF definitions
@@ -1304,51 +1337,47 @@ inline std::vector<ErlNifFunc> &get_erl_nif_funcs() {
13041337 return Registration::erl_nif_funcs;
13051338}
13061339
1307- enum class CallbackStatus {
1308- UNIMPLEMENTED ,
1309- IMPLEMENTED ,
1310- };
1340+ inline decltype (auto ) get_erl_nif_load_callback() {
1341+ return Registration::erl_nif_load_callback;
1342+ }
13111343
1312- template <CallbackStatus> struct OnLoad {
1313- static void on_load (ErlNifEnv *, void **, ERL_NIF_TERM ) {}
1314- };
1344+ inline decltype ( auto ) get_erl_nif_unload_callback() {
1345+ return Registration::erl_nif_unload_callback;
1346+ }
13151347
1316- template <CallbackStatus> struct OnUnload {
1317- static void on_unload (ErlNifEnv *, void *) {}
1318- };
1319- } // namespace __private__
1348+ inline int load (ErlNifEnv *caller_env, void **priv_data,
1349+ ERL_NIF_TERM load_info) noexcept {
1350+ init_atoms (caller_env);
13201351
1321- // Macros
1352+ if (!init_resources (caller_env)) {
1353+ return -1 ;
1354+ }
13221355
1323- #define FINE_LOAD (function ) \
1324- static_assert (std::is_invocable_v<decltype ((function)), ErlNifEnv *, \
1325- void **, ERL_NIF_TERM>, \
1326- "function must have the signature: void(*)(ErlNifEnv* " \
1327- " caller_env, void ** priv_data, fine::Term load_info)"); \
1328- template <> \
1329- struct fine ::__private__::OnLoad< \
1330- fine::__private__::CallbackStatus::IMPLEMENTED > { \
1331- static void on_load (ErlNifEnv *caller_env, void **priv_data, \
1332- ERL_NIF_TERM load_info) { \
1333- (function)(caller_env, priv_data, load_info); \
1334- } \
1335- }; \
1336- static_assert (true , " require a semicolon after the macro" )
1356+ try {
1357+ if (fine::__private__::get_erl_nif_load_callback ()) {
1358+ std::invoke (fine::__private__::get_erl_nif_load_callback (), caller_env,
1359+ priv_data, load_info);
1360+ }
1361+ } catch (const std::exception &e) {
1362+ enif_fprintf (stderr, " unhandled exception: %s\n " , e.what ());
1363+ return -1 ;
1364+ } catch (...) {
1365+ enif_fprintf (stderr, " unhandled throw\n " );
1366+ return -1 ;
1367+ }
13371368
1338- #define FINE_UNLOAD (function ) \
1339- static_assert ( \
1340- std::is_invocable_v<decltype ((function)), ErlNifEnv *, void *>, \
1341- "function must have the signature: void(*)(ErlNifEnv* " \
1342- " caller_env, void * priv_data)"); \
1343- template <> \
1344- struct fine ::__private__::OnUnload< \
1345- fine::__private__::CallbackStatus::IMPLEMENTED > { \
1346- static void on_unload (ErlNifEnv *caller_env, void *priv_data) { \
1347- (function)(caller_env, priv_data); \
1348- } \
1349- }; \
1350- static_assert (true , " require a semicolon after the macro" )
1369+ return 0 ;
1370+ }
13511371
1372+ inline void unload (ErlNifEnv *caller_env, void *priv_data) noexcept {
1373+ if (fine::__private__::get_erl_nif_unload_callback ()) {
1374+ std::invoke (fine::__private__::get_erl_nif_unload_callback (), caller_env,
1375+ priv_data);
1376+ }
1377+ }
1378+ } // namespace __private__
1379+
1380+ // Macros
13521381#define FINE_NIF (name, flags ) \
13531382 static ERL_NIF_TERM name##_nif(ErlNifEnv *env, int argc, \
13541383 const ERL_NIF_TERM argv[]) { \
@@ -1383,44 +1412,15 @@ template <CallbackStatus> struct OnUnload {
13831412 auto num_funcs = static_cast <int >(nif_funcs.size ()); \
13841413 auto funcs = nif_funcs.data (); \
13851414 \
1386- const auto load = [](ErlNifEnv *caller_env, void **priv_data, \
1387- ERL_NIF_TERM load_info) noexcept { \
1388- fine::__private__::init_atoms (caller_env); \
1389- \
1390- if (!fine::__private__::init_resources (caller_env)) { \
1391- return -1 ; \
1392- } \
1393- \
1394- try { \
1395- fine::__private__:: \
1396- OnLoad<fine::__private__::CallbackStatus::IMPLEMENTED >::on_load ( \
1397- caller_env, priv_data, load_info); \
1398- } catch (const std::exception &e) { \
1399- enif_fprintf (stderr, " unhandled exception: %s\n " , e.what ()); \
1400- return -1 ; \
1401- } catch (...) { \
1402- enif_fprintf (stderr, " unhandled throw\n " ); \
1403- return -1 ; \
1404- } \
1405- \
1406- return 0 ; \
1407- }; \
1408- \
1409- const auto unload = [](ErlNifEnv *caller_env, void *priv_data) noexcept { \
1410- fine::__private__:: \
1411- OnUnload<fine::__private__::CallbackStatus::IMPLEMENTED >::on_unload ( \
1412- caller_env, priv_data); \
1413- }; \
1414- \
14151415 static ErlNifEntry entry = {ERL_NIF_MAJOR_VERSION , \
14161416 ERL_NIF_MINOR_VERSION , \
14171417 name, \
14181418 num_funcs, \
14191419 funcs, \
1420- load, \
1420+ fine::__private__:: load, \
14211421 NULL , \
14221422 NULL , \
1423- unload, \
1423+ fine::__private__:: unload, \
14241424 ERL_NIF_VM_VARIANT , \
14251425 1 , \
14261426 sizeof (ErlNifResourceTypeInit), \
0 commit comments