Skip to content

Commit 4f2a521

Browse files
Implicit Conversion for fine::Ok and fine::Error (#13)
1 parent 983fadb commit 4f2a521

File tree

5 files changed

+97
-18
lines changed

5 files changed

+97
-18
lines changed

README.md

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -476,29 +476,37 @@ to usual Elixir functions. Fine provides `Ok<Args...>` and `Error<Args...>`
476476
types for this purpose.
477477
478478
```c++
479-
fine::Ok<>()
479+
fine::Ok<> example() {
480+
return fine::Ok();
481+
}
480482
// :ok
481483
482-
fine::Ok<int64_t>(1)
484+
fine::Ok<int64_t> example() {
485+
return fine::Ok(1);
486+
}
483487
// {:ok, 1}
484488
485-
fine::Error<>()
489+
fine::Error<> example() {
490+
return fine::Error();
491+
}
486492
// :error
487493
488-
fine::Error<std::string>("something went wrong")
494+
fine::Error<std::string> example() {
495+
return fine::Error("something went wrong");
496+
}
489497
// {:error, "something went wrong"}
490498
```
491499
492500
You can use `std::variant` to express a union of possible result types
493501
a NIF may return:
494502
495503
```c++
496-
std::variant<fine::Ok<int64_t>, fine::Error<std::string>> find_meaning(ErlNifEnv *env) {
497-
if (...) {
498-
return fine::Error<std::string>("something went wrong");
504+
std::variant<fine::Ok<int64_t, int64_t>, fine::Error<std::string>> divmod(ErlNifEnv *env, int64_t a, int64_t b) {
505+
if (b == 0) {
506+
return fine::Error("division by zero");
499507
}
500508
501-
return fine::Ok<int64_t>(42);
509+
return fine::Ok(a / b, a % b);
502510
}
503511
```
504512

c_include/fine.hpp

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -175,23 +175,47 @@ inline static bool operator>=(const fine::Term &lhs,
175175
// Represents a `:ok` tagged tuple, useful as a NIF result.
176176
template <typename... Args> class Ok {
177177
public:
178-
Ok(const Args &...items) : items(items...) {}
178+
using Items = std::tuple<Args...>;
179179

180-
private:
181-
friend struct Encoder<Ok<Args...>>;
180+
explicit Ok(Args... items) : m_items{std::move(items)...} {}
181+
182+
template <typename... UArgs>
183+
Ok(const Ok<UArgs...> &other) : m_items(other.items()) {}
184+
185+
template <typename... UArgs>
186+
Ok(Ok<UArgs...> &&other) : m_items(std::move(other).items()) {}
187+
188+
const Items &items() const & noexcept { return m_items; }
182189

183-
std::tuple<Args...> items;
190+
Items &items() & noexcept { return m_items; }
191+
192+
Items &&items() && noexcept { return std::move(m_items); }
193+
194+
private:
195+
Items m_items;
184196
};
185197

186198
// Represents a `:error` tagged tuple, useful as a NIF result.
187199
template <typename... Args> class Error {
188200
public:
189-
Error(const Args &...items) : items(items...) {}
201+
using Items = std::tuple<Args...>;
190202

191-
private:
192-
friend struct Encoder<Error<Args...>>;
203+
explicit Error(Args... items) : m_items{std::move(items)...} {}
204+
205+
template <typename... UArgs>
206+
Error(const Error<UArgs...> &other) : m_items(other.items()) {}
207+
208+
template <typename... UArgs>
209+
Error(Error<UArgs...> &&other) : m_items(std::move(other).items()) {}
210+
211+
const Items &items() const & noexcept { return m_items; }
193212

194-
std::tuple<Args...> items;
213+
Items &items() & noexcept { return m_items; }
214+
215+
Items &&items() && noexcept { return std::move(m_items); }
216+
217+
private:
218+
Items m_items;
195219
};
196220

197221
namespace __private__ {
@@ -940,7 +964,7 @@ template <typename... Args> struct Encoder<Ok<Args...>> {
940964
auto tag = __private__::atoms::ok;
941965

942966
if constexpr (sizeof...(Args) > 0) {
943-
return fine::encode(env, std::tuple_cat(std::tuple(tag), ok.items));
967+
return fine::encode(env, std::tuple_cat(std::tuple(tag), ok.items()));
944968
} else {
945969
return fine::encode(env, tag);
946970
}
@@ -952,7 +976,7 @@ template <typename... Args> struct Encoder<Error<Args...>> {
952976
auto tag = __private__::atoms::error;
953977

954978
if constexpr (sizeof...(Args) > 0) {
955-
return fine::encode(env, std::tuple_cat(std::tuple(tag), error.items));
979+
return fine::encode(env, std::tuple_cat(std::tuple(tag), error.items()));
956980
} else {
957981
return fine::encode(env, tag);
958982
}

test/c_src/finest.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ struct ExError {
7777
static constexpr auto is_exception = true;
7878
};
7979

80+
template <typename T, typename E>
81+
using Result = std::variant<fine::Ok<T>, fine::Error<E>>;
82+
8083
int64_t add(ErlNifEnv *, int64_t x, int64_t y) { return x + y; }
8184
FINE_NIF(add, 0);
8285

@@ -150,6 +153,37 @@ codec_tuple_int64_and_string(ErlNifEnv *,
150153
}
151154
FINE_NIF(codec_tuple_int64_and_string, 0);
152155

156+
Result<std::string, int64_t> codec_result_string_int64_ok(ErlNifEnv *,
157+
std::string term) {
158+
return fine::Ok(term);
159+
}
160+
FINE_NIF(codec_result_string_int64_ok, 0);
161+
162+
Result<std::string, int64_t> codec_result_string_int64_error(ErlNifEnv *,
163+
int64_t term) {
164+
return fine::Error(term);
165+
}
166+
FINE_NIF(codec_result_string_int64_error, 0);
167+
168+
Result<std::string, int64_t>
169+
codec_result_string_int64_ok_conversion(ErlNifEnv *) {
170+
return fine::Ok("fine");
171+
}
172+
FINE_NIF(codec_result_string_int64_ok_conversion, 0);
173+
174+
Result<std::string, int64_t>
175+
codec_result_string_int64_error_conversion(ErlNifEnv *) {
176+
uint16_t result = 42;
177+
return fine::Error(result);
178+
}
179+
FINE_NIF(codec_result_string_int64_error_conversion, 0);
180+
181+
std::variant<fine::Ok<int64_t, std::string>, fine::Error<>>
182+
codec_result_int64_string_void_ok_conversion(ErlNifEnv *) {
183+
return fine::Ok(static_cast<int32_t>(201702), "c++17");
184+
}
185+
FINE_NIF(codec_result_int64_string_void_ok_conversion, 0);
186+
153187
std::vector<int64_t> codec_vector_int64(ErlNifEnv *,
154188
std::vector<int64_t> term) {
155189
return term;

test/lib/finest/nif.ex

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ defmodule Finest.NIF do
2929
def codec_optional_int64(_term), do: err!()
3030
def codec_variant_int64_or_string(_term), do: err!()
3131
def codec_tuple_int64_and_string(_term), do: err!()
32+
def codec_result_string_int64_ok(_term), do: err!()
33+
def codec_result_string_int64_error(_term), do: err!()
34+
def codec_result_string_int64_ok_conversion(), do: err!()
35+
def codec_result_string_int64_error_conversion(), do: err!()
36+
def codec_result_int64_string_void_ok_conversion(), do: err!()
3237
def codec_vector_int64(_term), do: err!()
3338
def codec_vector_int64_alloc(_term), do: err!()
3439
def codec_map_atom_int64(_term), do: err!()

test/test/finest_test.exs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,14 @@ defmodule FinestTest do
223223
assert NIF.codec_error_empty() == :error
224224
assert NIF.codec_error_string("this is the reason") == {:error, "this is the reason"}
225225
end
226+
227+
test "result" do
228+
assert NIF.codec_result_string_int64_ok("fine") == {:ok, "fine"}
229+
assert NIF.codec_result_string_int64_error(42) == {:error, 42}
230+
assert NIF.codec_result_string_int64_ok_conversion() == {:ok, "fine"}
231+
assert NIF.codec_result_string_int64_error_conversion() == {:error, 42}
232+
assert NIF.codec_result_int64_string_void_ok_conversion() == {:ok, 201_702, "c++17"}
233+
end
226234
end
227235

228236
describe "resource" do

0 commit comments

Comments
 (0)