C++ Logo

std-discussion

Advanced search

Random thought: consteval member data

From: Scott Michaud <scott_at_[hidden]>
Date: Sun, 8 Aug 2021 15:08:00 -0400
I had a random thought while drinking my Friday coffee. *This is not a
proposal*. I discussed it on a C++ Discord, but it was met with
criticism. That said, sometimes even the biggest failures can lead to
interesting successes, so I'm curious what the std-discussion list
thinks about it.

The concept is compile-time data, which I'm calling "consteval members".
/*Its goal is to simplify some of the situations where templates and
macros are abused*/ by letting people express their intent in a more
literal, OOP fashion using data that the compiler cannot allow in the
actual assembly. This can be used as better control flow based on the
specific value of literals, and it can be used for better compile-time
error handling.

The key is that the transforms */must/* happen at compile-time. The
consteval data */cannot/* be in the application at runtime.

Some concerns:

  * I expect that this would be horrifyingly painful to implement in an
    actual C++ compiler.
  * This might be difficult for debuggers to implement... although I'd
    expect it would be able to infer the consteval state from the source
    code.

On the plus side, I also believe that it allows very readable and
maintainable code, especially for library authors that hide its usage
with access specifiers, so it might lead into some interesting ideas.

This email will show a use case where a factory-style object collects
properties and produces an object instance ("the builder pattern"). This
mechanism will allow the various functions to guide the user (with
compiler errors) if they forgot required data, or if they set
incompatible properties. In this case, the user must supply an IP
address and a port, but they are allowed to choose any mechanism that
does that, and (in this example) they can do so in any order (although
another use case could easily check for order).

class BuildableEndpoint {
*consteval bool hasAddress; //Cannot be used at runtime. Does not appear
in runtime object.**
** consteval bool hasPort; // Cannot be used at runtime. Does not
appear in runtime object.*

   std::unique_ptr<InternetAddress> address;
   uint16_t port;

   // I'm guessing constexpr qualifier would be necessary if the
argument affected the consteval members.
   BuildableEndpoint& AddIpV6AddressFromString(std::string argAddress)
   {
     static_assert(hasAddress == false, "Endpoint was already assigned
an IP address.");
     address = ParseIpV6Address(argAddress);
     hasAddress = true;
     return *this;
   }

   BuildableEndpoint& AddIpV4AddressFromString(std::string argAddress)
   {
     static_assert(hasAddress == false, "Endpoint was already assigned
an IP address.");
     address = ParseIpV4Address(argAddress);
     hasAddress = true;
     return *this;
   }

   BuildableEndpoint& AddIpV4AddressFromMask(uint8_t A, uint8_t B,
uint8_t C, uint8_t D)
   {
     static_assert(hasAddress == false, "Endpoint was already assigned
an IP address.");
     address = std::make_unique<SomeIpV4Address>(A, B, C, D);
     hasAddress = true;
     return *this;
   }

   BuildableEndpoint& AddPort(uint16_t argPort)
   {
     static_assert(hasPort == false, "Endpoint was already assigned a
port.");
     port = argPort;
     hasPort = true;
   }

   MyEndpoint Build()
   {
     static_assert(hasAddress, "Endpoint must have an address before
building."); //OK
     static_assert(hasPort, "Endpoint must be assigned a port before
building."); //OK

     return MyEndpoint(address, port);
   }
};

int main()
{
// This is okay. It has both an address (IPv4 or IPv6 doesn't matter)
and a port.
   MyEndpoint endpoint1 = BuildableEndpoint()
     .AddIpV4AddressFromString("10.45.20.12")
     .AddPort(1022)
     .Build();

// This is also okay.
   MyEndpoint endpoint2 = BuildableEndpoint()
     .AddIpV4AddressFromMask(10, 45, 20, 12)
     .AddPort(1022)
     .Build();

// This will trigger the hasPort static assert in Build() and not compile.
// MyEndpoint endpoint = BuildableEndpoint()
// .AddIpV4AddressFromMask(10, 45, 20, 12)
// .Build();
}





Received on 2021-08-08 14:08:03