Date: Wed, 29 May 2024 21:28:09 +0200
Hi,
You have an XY-problem.
On Wed, May 29, 2024 at 12:38 PM Frederick Virchanza Gotham via
Std-Proposals <std-proposals_at_[hidden]> wrote:
> class RS232 : public IRS232 {};
> virtual ~RS232(void) noexcept { this->Close(); }
> RS232_Microcontroller::~RS232_Microcontroller must call
> "this->Close()"
> RS232_Win32::~RS232_Win32 must call "this->Close()"
> RS232_Boost::~RS232_Boost must call "this->Close()"
>
The things you'd like to run *before* destructor is not in the *base*, but
in the *descendant*.
Thus, you *can* have a base for the interface:
class IRS232 { /* common stuff, any virtuals if you don't like concepts */
};
You have an implementation hierarchy:
class RS232 : public IRS232 { /* dtor doesn't call close */ };
You have the system-specific implementations of any number of layers:
class RS232_Arch_Impl : public RS232 { /* dtor still doesn't call close */
};
And you have the top layer:
template<typename T>
struct RS232_Top : T {
~RS232_Top { Close(); } // you're free to have preClose(), Close(),
afterClose(), etc.
};
using RS232_Arch = RS232_Top<RS232_Arch_Impl>;
This ensures that Close() is called *before* dtor of your implementation.
And it's done.
As for the proposed feature,
> virtual ~IRS232(void) noexcept derived_invokes(Close) {}
>
[...]
> The purpose of this proposed new feature is that instead of creating
> bugs that are discovered a runtime, you get a compiler error.
It's equivalent to the halting problem (i.e., Turing-complete) to check for
an arbitrary code whether it calls a function. (It could call it
conditionally e.g. in a loop or it could be called from a function / lambda
it conditionally calls, or via a function ptr, or via a pointer that was
written to a file and then read back, etc.) So it cannot be verified in
compile-time.
Also, since it's 2024..., I'd consider twice before writing virtual for an
interface. We have CRTP, concepts, even deducing this. I'd argue that, on
most architectures virtual is nowadays mainly for forward compatibility,
i.e., to be able to support descendants whose implementation is not yet
available at the time you compile the calling code (a notable exception to
this is cacheless MIPS architectures). That simplifies the code even more I
think.
Thanks,
-lorro
You have an XY-problem.
On Wed, May 29, 2024 at 12:38 PM Frederick Virchanza Gotham via
Std-Proposals <std-proposals_at_[hidden]> wrote:
> class RS232 : public IRS232 {};
> virtual ~RS232(void) noexcept { this->Close(); }
> RS232_Microcontroller::~RS232_Microcontroller must call
> "this->Close()"
> RS232_Win32::~RS232_Win32 must call "this->Close()"
> RS232_Boost::~RS232_Boost must call "this->Close()"
>
The things you'd like to run *before* destructor is not in the *base*, but
in the *descendant*.
Thus, you *can* have a base for the interface:
class IRS232 { /* common stuff, any virtuals if you don't like concepts */
};
You have an implementation hierarchy:
class RS232 : public IRS232 { /* dtor doesn't call close */ };
You have the system-specific implementations of any number of layers:
class RS232_Arch_Impl : public RS232 { /* dtor still doesn't call close */
};
And you have the top layer:
template<typename T>
struct RS232_Top : T {
~RS232_Top { Close(); } // you're free to have preClose(), Close(),
afterClose(), etc.
};
using RS232_Arch = RS232_Top<RS232_Arch_Impl>;
This ensures that Close() is called *before* dtor of your implementation.
And it's done.
As for the proposed feature,
> virtual ~IRS232(void) noexcept derived_invokes(Close) {}
>
[...]
> The purpose of this proposed new feature is that instead of creating
> bugs that are discovered a runtime, you get a compiler error.
It's equivalent to the halting problem (i.e., Turing-complete) to check for
an arbitrary code whether it calls a function. (It could call it
conditionally e.g. in a loop or it could be called from a function / lambda
it conditionally calls, or via a function ptr, or via a pointer that was
written to a file and then read back, etc.) So it cannot be verified in
compile-time.
Also, since it's 2024..., I'd consider twice before writing virtual for an
interface. We have CRTP, concepts, even deducing this. I'd argue that, on
most architectures virtual is nowadays mainly for forward compatibility,
i.e., to be able to support descendants whose implementation is not yet
available at the time you compile the calling code (a notable exception to
this is cacheless MIPS architectures). That simplifies the code even more I
think.
Thanks,
-lorro
Received on 2024-05-29 19:28:22