Date: Wed, 4 Jan 2023 15:55:39 +0000
It would be nice if we could output an 'std::any' to an 'std::ostream'
without having to use an 'std::any_cast". Specifically it would be
nice if we could do the following:
int main(void)
{
std::any a;
a = 5;
cout << a << endl;
a = std::string("dog");
cout << a << endl;
a = "fish";
cout << a << endl;
a = 5.2L;
cout << a << endl;
a = std::filesystem::temp_directory_path();
cout << a << endl;
}
I've written a new Standard header file called <any_ostream> which
makes the above code snippet possible. You can see it on GodBolt:
https://godbolt.org/z/q11oYzqsc
I could go a step further and make it work with user-defined classes;
in order to get this to work there would have to be a global container
containing elements of type pair<
type_info*,function<void(ostream&,void*)> > -- and for this to work
properly in multithreaded programs then either:
(a) The container must be populated from the main thread before
any other thread is spawned
(b) Protect the container with a mutex
I have copy-pasted the code from GodBolt below:
#include <any> // any, any_cast
#include <type_traits> // is_same_v, is_const_v, remove_volatile_t,
remove_reference_t, remove_cvref_t, conditional_t
#include <ostream> // ostream
// The following 3 headers are required for the definitions
// of classes which can be outputted to an ostream
#include <complex> // complex<>
#include <thread> // thread::id
#include <filesystem> // filesystem::path
#include <string> // string
#include <string_view> // string_view
class anyer_success {};
template<typename T>
requires std::is_same_v< T, std::remove_volatile_t<
std::remove_reference_t<T> > > // make sure it's non-volatile non-ref
struct Proxy {
Proxy(void) = delete;
Proxy(Proxy const &) = delete;
Proxy(Proxy &&) = delete;
Proxy &operator=(Proxy const &) = delete;
Proxy &operator=(Proxy &&) = delete;
Proxy const volatile *operator&(void) const volatile = delete;
template<typename U> void operator,(U&&) = delete;
T *p; // might be a pointer to const
Proxy(T *const arg) : p(arg) {} // argument might be a pointer to const
};
class AnyerNonConst {
AnyerNonConst(void) = delete;
AnyerNonConst(AnyerNonConst const &) = delete;
AnyerNonConst(AnyerNonConst &&) = delete;
AnyerNonConst &operator=(AnyerNonConst const &) = delete;
AnyerNonConst &operator=(AnyerNonConst &&) = delete;
AnyerNonConst const volatile *operator&(void) const volatile = delete;
template<typename U> void operator,(U&&) = delete;
std::any &a;
public:
AnyerNonConst(std::any &arg) : a(arg) {}
template<typename T>
requires std::is_same_v< T, std::remove_cvref_t<T> > // make sure
it's non-const non-volatile non-ref
Proxy<T> get(void)
{
return Proxy<T>( std::any_cast<T>( &this->a ) ); // return
value optimisation from C++17 onward
}
};
class AnyerConst {
AnyerConst(void) = delete;
AnyerConst(AnyerConst const &) = delete;
AnyerConst(AnyerConst &&) = delete;
AnyerConst &operator=(AnyerConst const &) = delete;
AnyerConst &operator=(AnyerConst &&) = delete;
AnyerConst const volatile *operator&(void) const volatile = delete;
template<typename U> void operator,(U&&) = delete;
std::any const &a;
public:
AnyerConst(std::any const &arg) : a(arg) {}
template<typename T>
requires std::is_same_v< T, std::remove_cvref_t<T> > // make sure
it's non-const non-volatile non-ref
Proxy<T const> get(void) /* Note that this is not a const member
function (i.e. *this is non-const) */
{
return Proxy<T const>{ std::any_cast<T const>( &this->a ) };
}
};
// The function on the next line gives us either an AnyerConst or AnyerNonConst
// depending on whether the argument is a ref-to-const or ref-to-nonconst
template<typename T>
requires std::is_same_v< T, std::remove_volatile_t<
std::remove_reference_t<T> > > // make sure it's non-volatile non-ref
inline std::conditional_t< std::is_const_v<T>, AnyerConst,
AnyerNonConst > Anyer(T &arg)
{
return std::conditional_t< std::is_const_v<T>, AnyerConst,
AnyerNonConst >(arg); // return value optimisation from C++17 onward
}
template<typename T>
std::ostream &operator<<(std::ostream &os, Proxy<T> ap) // T might be
const here
{
if ( nullptr == ap.p ) return os;
os << *ap.p;
throw anyer_success();
}
std::ostream &operator<<(std::ostream &os, std::any const &arg)
{
auto a = Anyer(arg); // 'a' will be either an AnyerConst or an
AnyerNonConst
try
{
os
// First all the instrinsics
<< a.get<bool>()
<< a.get<int>()
<< a.get<char>()
//<< a.get<char16_t>()
//<< a.get<char32_t>()
//<< a.get<char8_t>()
<< a.get<short>()
<< a.get<signed char>()
<< a.get<double>()
<< a.get<float>()
<< a.get<long>()
<< a.get<long double>()
<< a.get<long long>()
<< a.get<nullptr_t>()
<< a.get<unsigned char>()
<< a.get<unsigned int>()
<< a.get<unsigned long>()
<< a.get<unsigned long long>()
<< a.get<unsigned short>()
//<< a.get<wchar_t>()
// Now all the pointers
<< a.get<char const*>()
//<< a.get<char16_t*>()
//<< a.get<char32_t*>()
//<< a.get<char8_t*>()
<< a.get<signed char const*>()
<< a.get<unsigned char const*>()
<< a.get<void const*>()
//<< a.get<wchar_t*>()
// Now the classes
<< a.get<std::complex<double>>()
<< a.get<std::complex<float>>()
<< a.get<std::complex<long double>>()
<< a.get<std::error_code>()
<< a.get<std::thread::id>()
<< a.get<std::filesystem::path>()
//<< a.get< shared_ptr<> >() -- I'll code this later
<< a.get<std::string>()
<< a.get<std::string_view>()
//<< a.get<std::wstring>()
// And now the one's unsupported by GodBolt
/*
<< a.get<std::address>()
<< a.get<std::address_v4>()
<< a.get<std::address_v6>()
<< a.get<std::annotate_base>()
<< a.get<std::annotate_base>()
<< a.get<std::bool_set>()
<< a.get<std::network_v4>()
<< a.get<std::network_v6>()
*/
;
}
catch(anyer_success) {}
return os;
}
// = = = = = = = = Test code starts here = = = = = = = =
#include <iostream>
using std::cout;
using std::endl;
int main(void)
{
std::any a;
a = 5;
cout << a << endl;
a = std::string("dog");
cout << a << endl;
a = "fish";
cout << a << endl;
a = 5.2L;
cout << a << endl;
a = std::filesystem::temp_directory_path();
cout << a << endl;
}
without having to use an 'std::any_cast". Specifically it would be
nice if we could do the following:
int main(void)
{
std::any a;
a = 5;
cout << a << endl;
a = std::string("dog");
cout << a << endl;
a = "fish";
cout << a << endl;
a = 5.2L;
cout << a << endl;
a = std::filesystem::temp_directory_path();
cout << a << endl;
}
I've written a new Standard header file called <any_ostream> which
makes the above code snippet possible. You can see it on GodBolt:
https://godbolt.org/z/q11oYzqsc
I could go a step further and make it work with user-defined classes;
in order to get this to work there would have to be a global container
containing elements of type pair<
type_info*,function<void(ostream&,void*)> > -- and for this to work
properly in multithreaded programs then either:
(a) The container must be populated from the main thread before
any other thread is spawned
(b) Protect the container with a mutex
I have copy-pasted the code from GodBolt below:
#include <any> // any, any_cast
#include <type_traits> // is_same_v, is_const_v, remove_volatile_t,
remove_reference_t, remove_cvref_t, conditional_t
#include <ostream> // ostream
// The following 3 headers are required for the definitions
// of classes which can be outputted to an ostream
#include <complex> // complex<>
#include <thread> // thread::id
#include <filesystem> // filesystem::path
#include <string> // string
#include <string_view> // string_view
class anyer_success {};
template<typename T>
requires std::is_same_v< T, std::remove_volatile_t<
std::remove_reference_t<T> > > // make sure it's non-volatile non-ref
struct Proxy {
Proxy(void) = delete;
Proxy(Proxy const &) = delete;
Proxy(Proxy &&) = delete;
Proxy &operator=(Proxy const &) = delete;
Proxy &operator=(Proxy &&) = delete;
Proxy const volatile *operator&(void) const volatile = delete;
template<typename U> void operator,(U&&) = delete;
T *p; // might be a pointer to const
Proxy(T *const arg) : p(arg) {} // argument might be a pointer to const
};
class AnyerNonConst {
AnyerNonConst(void) = delete;
AnyerNonConst(AnyerNonConst const &) = delete;
AnyerNonConst(AnyerNonConst &&) = delete;
AnyerNonConst &operator=(AnyerNonConst const &) = delete;
AnyerNonConst &operator=(AnyerNonConst &&) = delete;
AnyerNonConst const volatile *operator&(void) const volatile = delete;
template<typename U> void operator,(U&&) = delete;
std::any &a;
public:
AnyerNonConst(std::any &arg) : a(arg) {}
template<typename T>
requires std::is_same_v< T, std::remove_cvref_t<T> > // make sure
it's non-const non-volatile non-ref
Proxy<T> get(void)
{
return Proxy<T>( std::any_cast<T>( &this->a ) ); // return
value optimisation from C++17 onward
}
};
class AnyerConst {
AnyerConst(void) = delete;
AnyerConst(AnyerConst const &) = delete;
AnyerConst(AnyerConst &&) = delete;
AnyerConst &operator=(AnyerConst const &) = delete;
AnyerConst &operator=(AnyerConst &&) = delete;
AnyerConst const volatile *operator&(void) const volatile = delete;
template<typename U> void operator,(U&&) = delete;
std::any const &a;
public:
AnyerConst(std::any const &arg) : a(arg) {}
template<typename T>
requires std::is_same_v< T, std::remove_cvref_t<T> > // make sure
it's non-const non-volatile non-ref
Proxy<T const> get(void) /* Note that this is not a const member
function (i.e. *this is non-const) */
{
return Proxy<T const>{ std::any_cast<T const>( &this->a ) };
}
};
// The function on the next line gives us either an AnyerConst or AnyerNonConst
// depending on whether the argument is a ref-to-const or ref-to-nonconst
template<typename T>
requires std::is_same_v< T, std::remove_volatile_t<
std::remove_reference_t<T> > > // make sure it's non-volatile non-ref
inline std::conditional_t< std::is_const_v<T>, AnyerConst,
AnyerNonConst > Anyer(T &arg)
{
return std::conditional_t< std::is_const_v<T>, AnyerConst,
AnyerNonConst >(arg); // return value optimisation from C++17 onward
}
template<typename T>
std::ostream &operator<<(std::ostream &os, Proxy<T> ap) // T might be
const here
{
if ( nullptr == ap.p ) return os;
os << *ap.p;
throw anyer_success();
}
std::ostream &operator<<(std::ostream &os, std::any const &arg)
{
auto a = Anyer(arg); // 'a' will be either an AnyerConst or an
AnyerNonConst
try
{
os
// First all the instrinsics
<< a.get<bool>()
<< a.get<int>()
<< a.get<char>()
//<< a.get<char16_t>()
//<< a.get<char32_t>()
//<< a.get<char8_t>()
<< a.get<short>()
<< a.get<signed char>()
<< a.get<double>()
<< a.get<float>()
<< a.get<long>()
<< a.get<long double>()
<< a.get<long long>()
<< a.get<nullptr_t>()
<< a.get<unsigned char>()
<< a.get<unsigned int>()
<< a.get<unsigned long>()
<< a.get<unsigned long long>()
<< a.get<unsigned short>()
//<< a.get<wchar_t>()
// Now all the pointers
<< a.get<char const*>()
//<< a.get<char16_t*>()
//<< a.get<char32_t*>()
//<< a.get<char8_t*>()
<< a.get<signed char const*>()
<< a.get<unsigned char const*>()
<< a.get<void const*>()
//<< a.get<wchar_t*>()
// Now the classes
<< a.get<std::complex<double>>()
<< a.get<std::complex<float>>()
<< a.get<std::complex<long double>>()
<< a.get<std::error_code>()
<< a.get<std::thread::id>()
<< a.get<std::filesystem::path>()
//<< a.get< shared_ptr<> >() -- I'll code this later
<< a.get<std::string>()
<< a.get<std::string_view>()
//<< a.get<std::wstring>()
// And now the one's unsupported by GodBolt
/*
<< a.get<std::address>()
<< a.get<std::address_v4>()
<< a.get<std::address_v6>()
<< a.get<std::annotate_base>()
<< a.get<std::annotate_base>()
<< a.get<std::bool_set>()
<< a.get<std::network_v4>()
<< a.get<std::network_v6>()
*/
;
}
catch(anyer_success) {}
return os;
}
// = = = = = = = = Test code starts here = = = = = = = =
#include <iostream>
using std::cout;
using std::endl;
int main(void)
{
std::any a;
a = 5;
cout << a << endl;
a = std::string("dog");
cout << a << endl;
a = "fish";
cout << a << endl;
a = 5.2L;
cout << a << endl;
a = std::filesystem::temp_directory_path();
cout << a << endl;
}
Received on 2023-01-04 15:55:52