66#include < cstdlib>
77#include < cstring>
88#include < erl_nif.h>
9+ #include < map>
910#include < optional>
1011#include < stdexcept>
1112#include < string>
@@ -50,22 +51,28 @@ class Atom {
5051 inline static std::vector<Atom *> atoms = {};
5152 inline static bool initialized = false ;
5253
53- const char * name;
54+ std::string name;
5455 std::optional<ERL_NIF_UINT> term;
5556
5657public:
57- Atom (const char * name) : name(name), term(std::nullopt ) {
58+ Atom (std::string name) : name(name), term(std::nullopt ) {
5859 if (!Atom::initialized) {
5960 Atom::atoms.push_back (this );
6061 }
6162 }
6263
6364 std::string to_string () const { return this ->name ; }
6465
66+ bool operator ==(const Atom &other) const { return this ->name == other.name ; }
67+
68+ bool operator ==(const char *other) const { return this ->name == other; }
69+
70+ bool operator <(const Atom &other) const { return this ->name < other.name ; }
71+
6572private:
6673 static void init_atoms (ErlNifEnv *env) {
6774 for (auto atom : Atom::atoms) {
68- atom->term = fine::__private__::make_atom (env, atom->name );
75+ atom->term = fine::__private__::make_atom (env, atom->name . c_str () );
6976 }
7077
7178 Atom::atoms.clear ();
@@ -205,6 +212,23 @@ template <> struct Decoder<double> {
205212 }
206213};
207214
215+ template <> struct Decoder <bool > {
216+ static bool decode (ErlNifEnv *env, const ERL_NIF_TERM &term) {
217+ char atom_string[6 ];
218+ auto length = enif_get_atom (env, term, atom_string, 6 , ERL_NIF_LATIN1);
219+
220+ if (length == 5 && strcmp (atom_string, " true" ) == 0 ) {
221+ return true ;
222+ }
223+
224+ if (length == 6 && strcmp (atom_string, " false" ) == 0 ) {
225+ return false ;
226+ }
227+
228+ throw std::invalid_argument (" decode failed, expected a boolean" );
229+ }
230+ };
231+
208232template <> struct Decoder <ErlNifPid> {
209233 static ErlNifPid decode (ErlNifEnv *env, const ERL_NIF_TERM &term) {
210234 ErlNifPid pid;
@@ -233,6 +257,24 @@ template <> struct Decoder<std::string> {
233257 }
234258};
235259
260+ template <> struct Decoder <Atom> {
261+ static Atom decode (ErlNifEnv *env, const ERL_NIF_TERM &term) {
262+ unsigned int length;
263+ if (!enif_get_atom_length (env, term, &length, ERL_NIF_LATIN1)) {
264+ throw std::invalid_argument (" decode failed, expected an atom" );
265+ }
266+
267+ auto buffer = std::make_unique<char []>(length + 1 );
268+
269+ // Note that enif_get_atom writes the NULL byte at the end
270+ if (!enif_get_atom (env, term, buffer.get (), length + 1 , ERL_NIF_LATIN1)) {
271+ throw std::invalid_argument (" decode failed, expected an atom" );
272+ }
273+
274+ return Atom (std::string (buffer.get (), length));
275+ }
276+ };
277+
236278template <typename T> struct Decoder <std::optional<T>> {
237279 static std::optional<T> decode (ErlNifEnv *env, const ERL_NIF_TERM &term) {
238280 char atom_string[4 ];
@@ -246,6 +288,29 @@ template <typename T> struct Decoder<std::optional<T>> {
246288 }
247289};
248290
291+ template <typename ... Args> struct Decoder <std::variant<Args...>> {
292+ static std::variant<Args...> decode (ErlNifEnv *env,
293+ const ERL_NIF_TERM &term) {
294+ return do_decode<Args...>(env, term);
295+ }
296+
297+ private:
298+ template <typename T, typename ... Rest>
299+ static std::variant<Args...> do_decode (ErlNifEnv *env,
300+ const ERL_NIF_TERM &term) {
301+ try {
302+ return fine::decode<T>(env, term);
303+ } catch (std::invalid_argument) {
304+ if constexpr (sizeof ...(Rest) > 0 ) {
305+ return do_decode<Rest...>(env, term);
306+ } else {
307+ throw std::invalid_argument (
308+ " decode failed, none of the variant types could be decoded" );
309+ }
310+ }
311+ }
312+ };
313+
249314template <typename ... Args> struct Decoder <std::tuple<Args...>> {
250315 static std::tuple<Args...> decode (ErlNifEnv *env, const ERL_NIF_TERM &term) {
251316 constexpr auto expected_size = sizeof ...(Args);
@@ -298,6 +363,37 @@ template <typename T> struct Decoder<std::vector<T>> {
298363 }
299364};
300365
366+ template <typename K, typename V> struct Decoder <std::map<K, V>> {
367+ static std::map<K, V> decode (ErlNifEnv *env, const ERL_NIF_TERM &term) {
368+ auto map = std::map<K, V>();
369+
370+ ERL_NIF_TERM key, value;
371+ ErlNifMapIterator iter;
372+ if (!enif_map_iterator_create (env, term, &iter,
373+ ERL_NIF_MAP_ITERATOR_FIRST)) {
374+ throw std::invalid_argument (" decode failed, expeted a map" );
375+ }
376+
377+ // Define RAII cleanup for the iterator
378+ auto cleanup = IterCleanup{env, iter};
379+
380+ while (enif_map_iterator_get_pair (env, &iter, &key, &value)) {
381+ map[fine::decode<K>(env, key)] = fine::decode<V>(env, value);
382+ enif_map_iterator_next (env, &iter);
383+ }
384+
385+ return map;
386+ }
387+
388+ private:
389+ struct IterCleanup {
390+ ErlNifEnv *env;
391+ ErlNifMapIterator iter;
392+
393+ ~IterCleanup () { enif_map_iterator_destroy (env, &iter); }
394+ };
395+ };
396+
301397template <typename T> struct Decoder <ResourcePtr<T>> {
302398 static ResourcePtr<T> decode (ErlNifEnv *env, const ERL_NIF_TERM &term) {
303399 void *ptr;
@@ -397,9 +493,9 @@ template <> struct Encoder<ErlNifPid> {
397493 }
398494};
399495
400- template <> struct Encoder <std:: nullopt_t > {
401- static ERL_NIF_TERM encode (ErlNifEnv *env, const std:: nullopt_t & nullopt ) {
402- return fine::encode (env, __private__::atoms::nil );
496+ template <> struct Encoder <ErlNifBinary > {
497+ static ERL_NIF_TERM encode (ErlNifEnv *env, const ErlNifBinary &binary ) {
498+ return enif_make_binary (env, const_cast <ErlNifBinary *>(&binary) );
403499 }
404500};
405501
@@ -408,7 +504,7 @@ template <> struct Encoder<std::string> {
408504 ERL_NIF_TERM term;
409505 auto data = enif_make_new_binary (env, string.length (), &term);
410506 if (data == nullptr ) {
411- throw std::runtime_error (" encode: failed to allocate new binary" );
507+ throw std::runtime_error (" encode failed, failed to allocate new binary" );
412508 }
413509 memcpy (data, string.data (), string.length ());
414510 return term;
@@ -420,11 +516,17 @@ template <> struct Encoder<Atom> {
420516 if (atom.term ) {
421517 return atom.term .value ();
422518 } else {
423- return fine::__private__::make_atom (env, atom.name );
519+ return fine::__private__::make_atom (env, atom.name . c_str () );
424520 }
425521 }
426522};
427523
524+ template <> struct Encoder <std::nullopt_t > {
525+ static ERL_NIF_TERM encode (ErlNifEnv *env, const std::nullopt_t &nullopt ) {
526+ return fine::encode (env, __private__::atoms::nil);
527+ }
528+ };
529+
428530template <typename T> struct Encoder <std::optional<T>> {
429531 static ERL_NIF_TERM encode (ErlNifEnv *env, const std::optional<T> &optional) {
430532 if (optional) {
@@ -435,6 +537,28 @@ template <typename T> struct Encoder<std::optional<T>> {
435537 }
436538};
437539
540+ template <typename ... Args> struct Encoder <std::variant<Args...>> {
541+ static ERL_NIF_TERM encode (ErlNifEnv *env,
542+ const std::variant<Args...> &variant) {
543+ return do_encode<Args...>(env, variant);
544+ }
545+
546+ private:
547+ template <typename T, typename ... Rest>
548+ static ERL_NIF_TERM do_encode (ErlNifEnv *env,
549+ const std::variant<Args...> &variant) {
550+ if (auto value = std::get_if<T>(&variant)) {
551+ return fine::encode (env, *value);
552+ }
553+
554+ if constexpr (sizeof ...(Rest) > 0 ) {
555+ return do_encode<Rest...>(env, variant);
556+ } else {
557+ throw std::runtime_error (" unreachable" );
558+ }
559+ }
560+ };
561+
438562template <typename ... Args> struct Encoder <std::tuple<Args...>> {
439563 static ERL_NIF_TERM encode (ErlNifEnv *env, const std::tuple<Args...> &tuple) {
440564 return do_encode (env, tuple, std::make_index_sequence<sizeof ...(Args)>());
@@ -451,49 +575,37 @@ template <typename... Args> struct Encoder<std::tuple<Args...>> {
451575 }
452576};
453577
454- template <typename ... Args> struct Encoder <Ok<Args...>> {
455- static ERL_NIF_TERM encode (ErlNifEnv *env, const Ok<Args...> &ok) {
456- auto tag = __private__::atoms::ok;
578+ template <typename T> struct Encoder <std::vector<T>> {
579+ static ERL_NIF_TERM encode (ErlNifEnv *env, const std::vector<T> &vector) {
580+ auto terms = std::vector<ERL_NIF_TERM>();
581+ terms.reserve (vector.size ());
457582
458- if constexpr (sizeof ...(Args) > 0 ) {
459- return fine::encode (env, std::tuple_cat (std::tuple (tag), ok.items ));
460- } else {
461- return fine::encode (env, tag);
583+ for (const auto &item : vector) {
584+ terms.push_back (fine::encode (env, item));
462585 }
463- }
464- };
465586
466- template <typename ... Args> struct Encoder <Error<Args...>> {
467- static ERL_NIF_TERM encode (ErlNifEnv *env, const Error<Args...> &error) {
468- auto tag = __private__::atoms::error;
469-
470- if constexpr (sizeof ...(Args) > 0 ) {
471- return fine::encode (env, std::tuple_cat (std::tuple (tag), error.items ));
472- } else {
473- return fine::encode (env, tag);
474- }
587+ return enif_make_list_from_array (env, terms.data (),
588+ static_cast <unsigned int >(terms.size ()));
475589 }
476590};
477591
478- template <typename ... Args> struct Encoder <std::variant<Args...>> {
479- static ERL_NIF_TERM encode (ErlNifEnv *env,
480- const std::variant<Args...> &variant) {
481- return do_encode<Args...>(env, variant);
482- }
592+ template <typename K, typename V> struct Encoder <std::map<K, V>> {
593+ static ERL_NIF_TERM encode (ErlNifEnv *env, const std::map<K, V> &map) {
594+ auto keys = std::vector<ERL_NIF_TERM>();
595+ auto values = std::vector<ERL_NIF_TERM>();
483596
484- private:
485- template <typename T, typename ... Rest>
486- static ERL_NIF_TERM do_encode (ErlNifEnv *env,
487- const std::variant<Args...> &variant) {
488- if (auto value = std::get_if<T>(&variant)) {
489- return fine::encode (env, *value);
597+ for (const auto &[key, value] : map) {
598+ keys.push_back (fine::encode (env, key));
599+ values.push_back (fine::encode (env, value));
490600 }
491601
492- if constexpr ( sizeof ...(Rest) > 0 ) {
493- return do_encode<Rest...>(env, variant);
494- } else {
495- throw std::runtime_error (" unreachable " );
602+ ERL_NIF_TERM map_term;
603+ if (! enif_make_map_from_arrays (env, keys. data (), values. data (), keys. size (),
604+ &map_term)) {
605+ throw std::runtime_error (" encode failed, failed to make a map " );
496606 }
607+
608+ return map_term;
497609 }
498610};
499611
@@ -531,7 +643,7 @@ struct Encoder<T, std::void_t<decltype(T::module), decltype(T::fields)>> {
531643 ERL_NIF_TERM map;
532644 if (!enif_make_map_from_arrays (env, keys, values,
533645 num_extra_fields + num_fields, &map)) {
534- throw std::runtime_error (" encode: failed to make a map" );
646+ throw std::runtime_error (" encode failed, failed to make a map" );
535647 }
536648
537649 return map;
@@ -570,6 +682,30 @@ struct Encoder<T, std::void_t<decltype(T::module), decltype(T::fields)>> {
570682 : std::true_type {};
571683};
572684
685+ template <typename ... Args> struct Encoder <Ok<Args...>> {
686+ static ERL_NIF_TERM encode (ErlNifEnv *env, const Ok<Args...> &ok) {
687+ auto tag = __private__::atoms::ok;
688+
689+ if constexpr (sizeof ...(Args) > 0 ) {
690+ return fine::encode (env, std::tuple_cat (std::tuple (tag), ok.items ));
691+ } else {
692+ return fine::encode (env, tag);
693+ }
694+ }
695+ };
696+
697+ template <typename ... Args> struct Encoder <Error<Args...>> {
698+ static ERL_NIF_TERM encode (ErlNifEnv *env, const Error<Args...> &error) {
699+ auto tag = __private__::atoms::error;
700+
701+ if constexpr (sizeof ...(Args) > 0 ) {
702+ return fine::encode (env, std::tuple_cat (std::tuple (tag), error.items ));
703+ } else {
704+ return fine::encode (env, tag);
705+ }
706+ }
707+ };
708+
573709namespace __private__ {
574710class ExceptionError : public std ::exception {
575711public:
@@ -711,6 +847,13 @@ ResourcePtr<T> make_resource(Args &&...args) {
711847 return resource;
712848}
713849
850+ template <typename T>
851+ Term make_resource_binary (ErlNifEnv *env, ResourcePtr<T> resource,
852+ const char *data, size_t size) {
853+ return enif_make_resource_binary (
854+ env, reinterpret_cast <void *>(resource.get ()), data, size);
855+ }
856+
714857class Registration {
715858 inline static std::vector<std::tuple<ErlNifResourceType **, const char *,
716859 void (*)(ErlNifEnv *, void *)>>
@@ -849,11 +992,19 @@ inline int load(ErlNifEnv *env, void **priv_data, ERL_NIF_TERM load_info) {
849992 {#name, fine::nif_arity (name), name##_nif, flags}); \
850993 static_assert (true , " require a semicolon after the macro" )
851994
995+ // Note that we use static, in case FINE_REASOURCE is used in another
996+ // translation unit on the same line
997+
852998#define FINE_RESOURCE (class_name ) \
853- auto __resource_registration_##class_name = \
999+ static auto __FINE_CONCAT__ ( __resource_registration_, __LINE__) = \
8541000 fine::Registration::register_resource<class_name>(#class_name); \
8551001 static_assert (true , " require a semicolon after the macro" )
8561002
1003+ // An extra level of indirection is necessary to make sure __LINE__
1004+ // is expanded before concatenation
1005+ #define __FINE_CONCAT__ (a, b ) __FINE_CONCAT_IMPL__(a, b)
1006+ #define __FINE_CONCAT_IMPL__ (a, b ) a##b
1007+
8571008// This is a modified version of ERL_NIF_INIT that points to the
8581009// registered NIF functions and also sets the load callback.
8591010#define FINE_INIT (name ) \
0 commit comments