Date: Fri, 9 Sep 2022 23:41:57 +0100
On Fri, Sep 9, 2022 at 11:47 AM Frederick Virchanza Gotham
<cauldwell.thomas_at_[hidden]> wrote:
>
> On Fri, Sep 9, 2022 at 9:58 AM Frederick Virchanza Gotham
>>
> > std::variant< std::specify_base<Mammal>, Dog, Cat > my_mammal;
> >
> > my_mammal.common_base()->Speak();
I realised just now that "std::variant" doesn't have a definition for
"operator->", and so I can simplify the syntax even further as
follows:
std::variant< std::specify_base<Mammal>, Dog, Cat > my_mammal;
my_mammal->Speak(); // throws 'bad_variant_access' if
there's no object
I have changed my code as follows:
#include <variant> // variant, visit, bad_variant_access
#include <type_traits> // is_base_of_v, remove_cvref_t, false_type, true_type
#include <tuple> // tuple, tuple_element_t
namespace detail {
template <class T, template <class...> class Template>
struct is_specialization : std::false_type {};
template <template <class...> class Template, class... Args>
struct is_specialization< Template<Args...>, Template > : std::true_type {};
template<class T, bool has_subtype>
struct subtype_or_void {
typedef void type;
};
template<class T>
struct subtype_or_void<T,true> {
typedef std::remove_cvref_t<typename T::type> type;
};
template<class T, bool has_subtype>
using subtype_or_void_t = typename subtype_or_void<T,has_subtype>::type;
} // close namespace 'detail'
template<class Base>
struct specify_base {
typedef std::remove_cvref_t<Base> type;
};
template<class... Types>
class my_variant : public std::variant<Types...> {
public:
using std::variant<Types...>::variant; // Inherit constructors
private:
using FirstType = std::tuple_element_t< 0u, std::tuple<Types...> >;
public:
static bool constexpr is_common_base_specified =
detail::is_specialization<FirstType, specify_base>{};
typedef detail::subtype_or_void_t<FirstType, is_common_base_specified> Base;
private:
static Base const volatile
*detail_common_base(std::variant<Types...> const volatile *const arg)
{
static_assert(detail::is_specialization<FirstType, specify_base>{},
"To invoke 'common_base()', the first type must "
"be a specialisation of 'specify_base'");
std::variant<Types...> *const p = const_cast<
std::variant<Types...> * >(arg);
auto my_lambda = []<class U>(U &u) -> Base*
{
if constexpr ( detail::is_specialization<U, specify_base>{} )
{
// Rather than return a nullptr when the
// currently hosted object is an 'std::specify_base<T>',
// throw an exception, so that we can always
// safely do:
// my_variant_object->Inherited_Method();
throw std::bad_variant_access();
}
else
{
static_assert( std::is_base_of_v<Base,U>,
"Base class specified to std::variant<
std::specify_base<T>, MoreTypes... > "
"is not a base class of the currently
hosted object");
return &u;
}
};
return std::visit<Base*>(my_lambda, *p);
}
public:
Base *operator->(void)
{
return const_cast<Base*>( detail_common_base(this) );
}
Base const *operator->(void) const
{
return const_cast<Base const *>( detail_common_base(this) );
}
Base volatile *operator->(void) volatile
{
return const_cast<Base volatile *>( detail_common_base(this) );
}
Base const volatile *operator->(void) const volatile
{
return const_cast<Base const volatile *>( detail_common_base(this) );
}
};
#include <iostream> // cout
struct Mammal { virtual void Speak(void) const volatile = 0; };
struct Dog : Mammal { void Speak(void) const volatile override {
std::cout << "Dog\n"; } };
struct Cat : Mammal { void Speak(void) const volatile override {
std::cout << "Cat\n"; } };
struct Fish {}; // not a mammal!
my_variant< specify_base<Mammal>, Dog, Cat > my_mammal;
int main(void)
{
my_mammal.emplace<2u>();
my_mammal->Speak();
// On the next line we can make a const volatile object too
my_variant< specify_base<Mammal>, Dog, Cat > const volatile my_cvmammal;
my_cvmammal->Speak(); // This will throw 'bad_variant_access'
// I would like if the following line didn't compile, but it does.
// Somehow I need to iterate through all the types and make sure
// that they are all derived from Mammal.
my_variant< specify_base<Mammal>, Dog, Cat, Fish > volatile
some_other_object;
// some_other_object->Speak(); - THIS WON'T COMPILE
my_variant<int,double,long> abc;
}
<cauldwell.thomas_at_[hidden]> wrote:
>
> On Fri, Sep 9, 2022 at 9:58 AM Frederick Virchanza Gotham
>>
> > std::variant< std::specify_base<Mammal>, Dog, Cat > my_mammal;
> >
> > my_mammal.common_base()->Speak();
I realised just now that "std::variant" doesn't have a definition for
"operator->", and so I can simplify the syntax even further as
follows:
std::variant< std::specify_base<Mammal>, Dog, Cat > my_mammal;
my_mammal->Speak(); // throws 'bad_variant_access' if
there's no object
I have changed my code as follows:
#include <variant> // variant, visit, bad_variant_access
#include <type_traits> // is_base_of_v, remove_cvref_t, false_type, true_type
#include <tuple> // tuple, tuple_element_t
namespace detail {
template <class T, template <class...> class Template>
struct is_specialization : std::false_type {};
template <template <class...> class Template, class... Args>
struct is_specialization< Template<Args...>, Template > : std::true_type {};
template<class T, bool has_subtype>
struct subtype_or_void {
typedef void type;
};
template<class T>
struct subtype_or_void<T,true> {
typedef std::remove_cvref_t<typename T::type> type;
};
template<class T, bool has_subtype>
using subtype_or_void_t = typename subtype_or_void<T,has_subtype>::type;
} // close namespace 'detail'
template<class Base>
struct specify_base {
typedef std::remove_cvref_t<Base> type;
};
template<class... Types>
class my_variant : public std::variant<Types...> {
public:
using std::variant<Types...>::variant; // Inherit constructors
private:
using FirstType = std::tuple_element_t< 0u, std::tuple<Types...> >;
public:
static bool constexpr is_common_base_specified =
detail::is_specialization<FirstType, specify_base>{};
typedef detail::subtype_or_void_t<FirstType, is_common_base_specified> Base;
private:
static Base const volatile
*detail_common_base(std::variant<Types...> const volatile *const arg)
{
static_assert(detail::is_specialization<FirstType, specify_base>{},
"To invoke 'common_base()', the first type must "
"be a specialisation of 'specify_base'");
std::variant<Types...> *const p = const_cast<
std::variant<Types...> * >(arg);
auto my_lambda = []<class U>(U &u) -> Base*
{
if constexpr ( detail::is_specialization<U, specify_base>{} )
{
// Rather than return a nullptr when the
// currently hosted object is an 'std::specify_base<T>',
// throw an exception, so that we can always
// safely do:
// my_variant_object->Inherited_Method();
throw std::bad_variant_access();
}
else
{
static_assert( std::is_base_of_v<Base,U>,
"Base class specified to std::variant<
std::specify_base<T>, MoreTypes... > "
"is not a base class of the currently
hosted object");
return &u;
}
};
return std::visit<Base*>(my_lambda, *p);
}
public:
Base *operator->(void)
{
return const_cast<Base*>( detail_common_base(this) );
}
Base const *operator->(void) const
{
return const_cast<Base const *>( detail_common_base(this) );
}
Base volatile *operator->(void) volatile
{
return const_cast<Base volatile *>( detail_common_base(this) );
}
Base const volatile *operator->(void) const volatile
{
return const_cast<Base const volatile *>( detail_common_base(this) );
}
};
#include <iostream> // cout
struct Mammal { virtual void Speak(void) const volatile = 0; };
struct Dog : Mammal { void Speak(void) const volatile override {
std::cout << "Dog\n"; } };
struct Cat : Mammal { void Speak(void) const volatile override {
std::cout << "Cat\n"; } };
struct Fish {}; // not a mammal!
my_variant< specify_base<Mammal>, Dog, Cat > my_mammal;
int main(void)
{
my_mammal.emplace<2u>();
my_mammal->Speak();
// On the next line we can make a const volatile object too
my_variant< specify_base<Mammal>, Dog, Cat > const volatile my_cvmammal;
my_cvmammal->Speak(); // This will throw 'bad_variant_access'
// I would like if the following line didn't compile, but it does.
// Somehow I need to iterate through all the types and make sure
// that they are all derived from Mammal.
my_variant< specify_base<Mammal>, Dog, Cat, Fish > volatile
some_other_object;
// some_other_object->Speak(); - THIS WON'T COMPILE
my_variant<int,double,long> abc;
}
Received on 2022-09-09 22:42:09