I am trying to create a map_error
method attached to a std::expected
type, or something similar. I can't seem to figure out the template meta programming. Is this possible to do something similar to this:
expect<int, fmt_err, io_err> square(int num) {
if (num % 1)
return fmt_err{};
else if (num < 5)
return io_err{};
else
return num * num;
}
square(i)
.map_error([](fmt_err err) {
return 10;
})
.map_error([](const io_error& err) {
return 10;
});
This is what I have so far, I can't seem to get it compiling though. For every .map_error
it should remove one type, and then return a sub type of errs
with the single error removed.
#include <type_traits>
#include <memory>
template<class...>struct types{using type=types;};
template<class Sig> struct args;
template<class R, class...Args>
struct args<R(Args...)>:types<Args...>{};
template<class Sig> using args_t=typename args<Sig>::type;
struct err
{
virtual ~err() {}
};
struct fmt_err : public err
{
};
struct io_err : public err
{
};
template<typename...> struct errs;
template<typename R, typename T, typename... Ts>
struct errs<R, T, Ts...> : public errs<R, Ts...>
{
using self = errs<T, Ts...>;
using args = errs<R, Ts...>;
using args::args;
errs(R&& result) : errs(std::move(result))
{
}
errs(T t)
{
}
};
template<typename R, typename T>
struct errs<R, T>
{
errs() = default;
errs(const R& result) : result(result)
{
}
errs(R&& result) : result(std::move(result))
{
}
errs(std::unique_ptr<err> error, int index)
: error(std::move(error))
, index(index)
{
}
R result;
int index = -1;
std::unique_ptr<err> error;
};
template<typename...> struct expect;
template <typename T, typename E, typename... Es>
struct expect<T, E, Es...> : public expect<T, errs<T, E, Es...>>
{
using base = expect<T, errs<T, E, Es...>>;
using base::base;
expect(T&&) {
}
expect(E&&) {
}
// template<int N, typename... Ts> using NthTypeOf = typename std::tuple_element<N, std::tuple<Ts...>>::type;
// template<typename En = std::enable_if_t<true, NthTypeOf<0, Es...>>>
// expect(En&&) : expect(std::make_unique<En>(), sizeof...(Es)) {
// }
};
template <typename T, typename E, typename... Es>
struct expect<T, errs<T, E, Es...>>
{
errs<T, E, Es...> errors;
expect() {
}
expect(T&&)
{
}
expect(std::unique_ptr<err>&& error, int index)
{
errors.error = std::move(error);
}
template<typename F>
expect<T, Es...> map_error(F&& func) {
if (errors.index == 0) {
return errors.result;
} else if (errors.index == sizeof...(Es)) {
return func(*static_cast<E*>(errors.error.get()));
} else if (errors.index < sizeof...(Es)) {
return errors;
}
}
};
expect<int, fmt_err, io_err> square(int num) {
if (num % 1)
return fmt_err{};
else if (num < 5)
return io_err{};
else
return num * num;
}
int main()
{
for(int i = 0; i < 10; i)
{
square(i)
.map_error([](fmt_err err){
return 10;
});
}
}
CodePudding user response:
You are missing three constructors, here is a working version of your code: https://godbolt.org/z/cxY8Yzzq1
#include <memory>
#include <type_traits>
template <class...>
struct types {
using type = types;
};
template <class Sig>
struct args;
template <class R, class... Args>
struct args<R(Args...)> : types<Args...> {};
template <class Sig>
using args_t = typename args<Sig>::type;
struct err {
virtual ~err() {}
};
struct fmt_err : public err {};
struct io_err : public err {};
template <typename...>
struct errs;
template <typename R, typename T, typename... Ts>
struct errs<R, T, Ts...> : public errs<R, Ts...> {
using self = errs<T, Ts...>;
using args = errs<R, Ts...>;
using args::args;
errs(R&& result) : errs(std::move(result)) {}
errs(T t) {}
};
template <typename R, typename T>
struct errs<R, T> {
errs() = default;
// ---------------------------------
// needed in order to be able to make copies of errs, copying was disabled by storing a unique_ptr inside the class
errs(const errs& rref) :
result(rref.result),
index(rref.index),
error(new err(*rref.error))
{
}
// ---------------------------------
errs(const R& result) : result(result) {}
errs(R&& result) : result(std::move(result)) {}
errs(std::unique_ptr<err> error, int index)
: error(std::move(error)), index(index) {}
R result;
int index = -1;
std::unique_ptr<err> error;
};
template <typename...>
struct expect;
template <typename T, typename E, typename... Es>
struct expect<T, E, Es...> : public expect<T, errs<T, E, Es...>> {
using base = expect<T, errs<T, E, Es...>>;
using base::base;
expect(T&&) {}
expect(E&&) {}
// ---------------------------------
// needed in order to convert from io_err' to 'expect<int, fmt_err, io_err>' in line 123
expect(Es&&...) {}
// ---------------------------------
// template<int N, typename... Ts> using NthTypeOf = typename
// std::tuple_element<N, std::tuple<Ts...>>::type;
// template<typename En = std::enable_if_t<true, NthTypeOf<0, Es...>>>
// expect(En&&) : expect(std::make_unique<En>(), sizeof...(Es)) {
// }
};
template <typename T, typename E, typename... Es>
struct expect<T, errs<T, E, Es...>> {
errs<T, E, Es...> errors;
expect() {}
expect(T&&) {}
// ---------------------------------
// needed because you pass an int, not an rvalue in line 110 (return expect<T, Es...>(errors.result);)`
expect(const errs<T, E, Es...>& errors) : errors(errors) {}
// ---------------------------------
expect(std::unique_ptr<err>&& error, int index) {
errors.error = std::move(error);
}
template <typename F>
expect<T, Es...> map_error(F&& func) {
if (errors.index == 0) {
return expect<T, Es...>(errors.result);
} else if (errors.index == sizeof...(Es)) {
return func(*static_cast<E*>(errors.error.get()));
} else if (errors.index < sizeof...(Es)) {
return errors;
}
}
};
expect<int, fmt_err, io_err> square(int num) {
if (num % 1)
return fmt_err{};
else if (num < 5)
return io_err{};
else
return num * num;
}
int main() {
for (int i = 0; i < 10; i) {
square(i).map_error([](fmt_err err) { return 10; });
}
}
Missing constructors:
errs<R, T>::errs(const errs& rref); // needed in order to be able to make copies of errs, copying was disabled by storing a unique_ptr inside the class
expect<T, E, Es...>::expect(Es&&...) // needed in order to convert from io_err' to 'expect<int, fmt_err, io_err>' in line 123
expect<T, errs<T, E, Es...>>::expect(const errs<T, E, Es...>& errors) // needed because you pass an int, not an rvalue in line 110