Date: Sun, 16 Jul 2023 14:56:46 +0100
On Sun, Jul 16, 2023 at 2:13 PM Arthur O'Dwyer wrote:
>
> I think this is a legitimately interesting "puzzle"; although I don't think it currently merits any change to the core language nor to the standard library.
I'm not sure if we're singing from the same hymn sheet here. . .
but anyway I figured out how to return a mutex by value from a
function, and to have that mutex placed inside a global
'std::optional' variable.
Check it out:
https://godbolt.org/z/5zqfb93bx
Next I will combine this code with my previous code so that we can:
Step 1 : Return a locked mutex by value from a function
Step 2 : Have that locked mutex placed inside a global
std::optional variable
And here's the GodBolt copy-pasted:
#include <optional> // optional
namespace detail {
// An object of type 'Invoker' is returned
// by value from PutRetvalInOptional::operator()
template<typename T, typename... Params>
class Invoker {
std::optional<T> *const popt = nullptr;
T (*const funcptr)(Params...);
public:
Invoker(std::optional<T> &argO, T (*const argF)(Params...)) :
popt(&argO), funcptr(argF) {}
void operator()(Params... args) const
{
// 'std::optional<T>' inherits privately from
'std::_Optional_base<T>'
auto &base =
*static_cast<std::_Optional_base<T>*>(static_cast<void*>(popt));
// The next two lines take advantage of a feature of the
// System V x86_64 calling convention. The return value
// can be placed as the first parameter (because both
// use the RDI register).
void (*const magic)(T*, Params...) = reinterpret_cast<void
(*)(T*, Params...)>(funcptr);
magic( &base._M_payload._M_payload._M_value,
std::forward<Params>(args)... );
// Lastly we need to manually set the 'has_value()' of the
'std::optional' to 'true'
base._M_payload._M_engaged = true;
}
Invoker(Invoker &&) = delete;
Invoker(Invoker const & ) = delete;
Invoker &operator=(Invoker &&) = delete;
Invoker &operator=(Invoker const & ) = delete;
};
} // close namespace 'detail'
template<typename T>
class PutRetvalInOptional {
std::optional<T> *const popt;
public:
PutRetvalInOptional(std::optional<T> &arg) : popt(&arg) {}
template<typename... Params>
detail::Invoker<T,Params...> operator()(T (*funcptr)(Params...))
{
return detail::Invoker<T,Params...>(*popt, funcptr);
}
PutRetvalInOptional(PutRetvalInOptional &&) = delete;
PutRetvalInOptional(PutRetvalInOptional const & ) = delete;
PutRetvalInOptional &operator=(PutRetvalInOptional &&) = delete;
PutRetvalInOptional &operator=(PutRetvalInOptional const & ) = delete;
};
// ===================== Here comes the test code:
==========================================
#include <iostream> // cout
using std::cout, std::endl;
struct Mutex {
// ---------- cannot move and cannot copy ----------
Mutex(Mutex &&) = delete;
Mutex(Mutex const & ) = delete;
Mutex &operator=(Mutex &&) = delete;
Mutex &operator=(Mutex const & ) = delete;
Mutex(void) { cout << "construct\n"; }
void lock(void) { cout << "lock\n" ; }
void unlock(void) { cout << "unlock\n" ; }
~Mutex(void) { cout << "destroy\n" ; }
};
Mutex GiveMeMutex(int const a, double const b, char const *const p)
// dummy parameters
{
cout << "Args: " << a << " " << b << " " << p << endl;
return Mutex(); // cannot be copied, cannot be moved
}
std::optional<Mutex> om;
int main(int const argc, char **const argv)
{
PutRetvalInOptional(om)(GiveMeMutex)(7,4.2,"Hello World");
// GiveMeMutex has returned a mutex by value
// ...and that mutex is now inside a global optional
om->lock();
om->unlock();
}
>
> I think this is a legitimately interesting "puzzle"; although I don't think it currently merits any change to the core language nor to the standard library.
I'm not sure if we're singing from the same hymn sheet here. . .
but anyway I figured out how to return a mutex by value from a
function, and to have that mutex placed inside a global
'std::optional' variable.
Check it out:
https://godbolt.org/z/5zqfb93bx
Next I will combine this code with my previous code so that we can:
Step 1 : Return a locked mutex by value from a function
Step 2 : Have that locked mutex placed inside a global
std::optional variable
And here's the GodBolt copy-pasted:
#include <optional> // optional
namespace detail {
// An object of type 'Invoker' is returned
// by value from PutRetvalInOptional::operator()
template<typename T, typename... Params>
class Invoker {
std::optional<T> *const popt = nullptr;
T (*const funcptr)(Params...);
public:
Invoker(std::optional<T> &argO, T (*const argF)(Params...)) :
popt(&argO), funcptr(argF) {}
void operator()(Params... args) const
{
// 'std::optional<T>' inherits privately from
'std::_Optional_base<T>'
auto &base =
*static_cast<std::_Optional_base<T>*>(static_cast<void*>(popt));
// The next two lines take advantage of a feature of the
// System V x86_64 calling convention. The return value
// can be placed as the first parameter (because both
// use the RDI register).
void (*const magic)(T*, Params...) = reinterpret_cast<void
(*)(T*, Params...)>(funcptr);
magic( &base._M_payload._M_payload._M_value,
std::forward<Params>(args)... );
// Lastly we need to manually set the 'has_value()' of the
'std::optional' to 'true'
base._M_payload._M_engaged = true;
}
Invoker(Invoker &&) = delete;
Invoker(Invoker const & ) = delete;
Invoker &operator=(Invoker &&) = delete;
Invoker &operator=(Invoker const & ) = delete;
};
} // close namespace 'detail'
template<typename T>
class PutRetvalInOptional {
std::optional<T> *const popt;
public:
PutRetvalInOptional(std::optional<T> &arg) : popt(&arg) {}
template<typename... Params>
detail::Invoker<T,Params...> operator()(T (*funcptr)(Params...))
{
return detail::Invoker<T,Params...>(*popt, funcptr);
}
PutRetvalInOptional(PutRetvalInOptional &&) = delete;
PutRetvalInOptional(PutRetvalInOptional const & ) = delete;
PutRetvalInOptional &operator=(PutRetvalInOptional &&) = delete;
PutRetvalInOptional &operator=(PutRetvalInOptional const & ) = delete;
};
// ===================== Here comes the test code:
==========================================
#include <iostream> // cout
using std::cout, std::endl;
struct Mutex {
// ---------- cannot move and cannot copy ----------
Mutex(Mutex &&) = delete;
Mutex(Mutex const & ) = delete;
Mutex &operator=(Mutex &&) = delete;
Mutex &operator=(Mutex const & ) = delete;
Mutex(void) { cout << "construct\n"; }
void lock(void) { cout << "lock\n" ; }
void unlock(void) { cout << "unlock\n" ; }
~Mutex(void) { cout << "destroy\n" ; }
};
Mutex GiveMeMutex(int const a, double const b, char const *const p)
// dummy parameters
{
cout << "Args: " << a << " " << b << " " << p << endl;
return Mutex(); // cannot be copied, cannot be moved
}
std::optional<Mutex> om;
int main(int const argc, char **const argv)
{
PutRetvalInOptional(om)(GiveMeMutex)(7,4.2,"Hello World");
// GiveMeMutex has returned a mutex by value
// ...and that mutex is now inside a global optional
om->lock();
om->unlock();
}
Received on 2023-07-16 13:56:56