Currently I have this code, which works fine:
#pragma once
#include <expected>
#include <wayland-server.h>
class Display
{
private:
struct wl_display *ptr;
public:
enum class Error
{
CannotCreateDisplay,
};
Display() = delete;
Display(struct wl_display *ptr)
: ptr{ptr}
{
}
Display(const Display &) = delete;
Display(Display &&) = delete;
Display &operator=(const Display &) = delete;
Display &operator=(Display &&) = delete;
~Display()
{
wl_display_destroy_clients(this->ptr);
wl_display_destroy(this->ptr);
}
static std::expected<Display, Error> create()
{
struct wl_display *ptr{wl_display_create()};
if (ptr == nullptr) {
return std::unexpected{Error::CannotCreateDisplay};
}
return {ptr};
}
};
I would like to make the Display(struct wl_display *)
constructor private but I can't because otherwise the create
would complains about not finding any viable constructor to call for Display
.
One solution I found, is to make a move constructor for Display
but is there any else solutions for this ?
class Display
{
private:
struct wl_display *ptr;
Display(struct wl_display *ptr)
: ptr{ptr}
{
}
public:
enum class Error
{
CannotCreateDisplay,
};
Display() = delete;
Display(const Display &) = delete;
Display(Display &&) = delete;
Display &operator=(const Display &) = delete;
Display &operator=(Display &&) = delete;
~Display()
{
wl_display_destroy_clients(this->ptr);
wl_display_destroy(this->ptr);
}
static std::expected<Display, Error> create()
{
struct wl_display *ptr{wl_display_create()};
if (ptr == nullptr) {
return std::unexpected{Error::CannotCreateDisplay};
}
return {ptr}; // ERROR: can't find any viable constructor
}
};
Surely you can, by making use of the monadic operations of the std::expected
. The same method can be applied to std::optional
.
Instead of
return {ptr};
You can detour via another std::expected
, and transform the value in-place (credit to @Artyer for making it more succinct):
return std::expected<void, Error>{}.transform(
[&] {
return Display{ptr};
}
);
Demo: https://godbolt.org/z/x4aoWsTeT
Here is how it works. Without the detour, you have to choose between constructing Display
directly inside std::expected
, or constructing Display
outside and move it.
The former requires invoking the private constructor directly inside the constructor of std::expected
. Since std::expected
is not a friend, this is doomed to fail.
The latter only works if Display
is movable. This is exactly what OP wants to avoid.
With the detour, we are providing a (sort of public) callback function which calls the private constructor in a context that it is allowed. This is similar to the passkey idiom mentioned in the comments: both provide a public construction function that has very limited access. In the passkey idiom, the access is protected by a struct that serves as an authorization token; In this method, the access is protected naturally by scope, as the lambda is a local variable.