/******************************************************************************
Welcome to GDB Online.
GDB online is an online compiler and debugger tool for C, C++, Python, PHP, Ruby,
C#, OCaml, VB, Perl, Swift, Prolog, Javascript, Pascal, HTML, CSS, JS
Code, Compile, Run and Debug online from anywhere in world.
*******************************************************************************/
#include <functional>
#include <iostream>
#include <memory>
#include <optional>
#include <string>
#include <type_traits>
// can_dereference
template <typename T>
struct can_dereference_helper
{
template <typename U, typename = decltype(*std::declval<U>())>
static std::true_type test(U);
template <typename...U>
static std::false_type test(U...);
using type = decltype(test(std::declval<T>()));
};
template <typename T>
struct can_dereference : can_dereference_helper<typename std::decay<T>::type>::type {};
// deref
template <typename FT, typename T>
auto deref(T&& t, std::false_type) -> std::optional<FT>
{
return std::forward<T>(t);
}
template <typename FT, typename T>
auto deref(T&& t) -> std::optional<FT>;
template <typename FT, typename T>
auto deref(T&& t, std::true_type) -> std::optional<FT>
{
if (t)
{
return deref<FT>(*std::forward<T>(t));
}
return std::nullopt;
}
template <typename FT, typename T>
auto deref(T&& t) -> std::optional<FT>
{
return deref<FT>(std::forward<T>(t), can_dereference<T>{});
}
// get_field
template <typename> struct is_optional : std::false_type {};
template <typename T> struct is_optional<std::optional<T>> : std::true_type {};
template <typename O, typename F>
auto convert_optional(O&& o, F&& f)
-> std::enable_if_t<
is_optional<std::decay_t<O>>::value,
std::optional<std::decay_t<decltype(std::invoke(std::forward<F>(f),
*std::forward<O>(o)))>>>
{
if (o)
{
return std::invoke(std::forward<F>(f), *o);
}
return std::nullopt;
}
template <typename O, typename F>
auto get_field(O&& o, F&& f)
-> decltype(convert_optional(std::forward<O>(o),
std::forward<F>(f)).value_or(std::nullopt))
{
return convert_optional(std::forward<O>(o),
std::forward<F>(f)).value_or(std::nullopt);
}
// Test data
struct Entry
{
std::optional<std::string> name;
};
struct Container
{
std::optional<std::shared_ptr<Entry>> entry;
};
// main
int main()
{
Container emptyContainer{};
Entry entry{"name"};
Container container{std::make_shared<Entry>(entry)};
std::cout << deref<Entry>(container.entry).has_value() << std::endl;
std::cout << deref<Entry>(emptyContainer.entry).has_value() << std::endl;
const auto name = get_field(deref<Entry>(container.entry), &Entry::name);
std::cout << name.value_or("empty") << std::endl;
const auto emptyName = get_field(deref<Entry>(emptyContainer.entry), &Entry::name);
std::cout << emptyName.value_or("empty") << std::endl;
return 0;
}