I'm implementing a constant/immutable class in C++ 20 and I'd like to make as much as possible constant and do some value checks in my factory functions.
Below is a simplified example (paste to godbolt.org) of what I'm trying to achieve. Please see the code comments on the options I'm considering.
Note that this is for an embedded project and using exceptions is not an option.
Any ideas on achieving good compile-time errors for bad values of "i" where the error msg. actually relates to to the value of i (without templates)?
Update We actually have some non constant uses of this class (where "i" is not constant) so it seems that consteval is out of the picture (together with the template case). Basically, the question is then: Is it possible to have compile-time errors if "i" is constant and runtime errors if "i" is not?
Update2 Updated code with option 5, based on comments below.
#include <cstdlib>
#define Assert while(true) {}
class ConstantClass
{
public:
static consteval ConstantClass ConstEvalFactory(int i)
{
if(i < 0)
{
// Option 1
// - Use #error
// - Always fails at compile time
// - even for valid i
// #error "Invalid value"
// Option 2
// - Non constant expression inside consteval
// - Compile-time error msg. do not relate to the value of i
Assert(false);
}
else
{
return ConstantClass(i);
}
}
template<int i>
static consteval ConstantClass TemplateConstEvalFactory()
{
// Option 5
// - template function with static assert
// - user will have to choose which factory to call
// - I.e. x<...>() or x(...)
static_assert(i>=0);
return ConstantClass(i);
}
static constexpr ConstantClass ConstExprFactory(int i)
{
if(i < 0)
{
// Option 3
// - Non constant expression inside constexpr
// - If i < 0, this will compile to non-constant and fail at runtime
// - Failure will depend on implementation of "Assert"
Assert(false);
}
else
{
return ConstantClass(i);
}
}
private:
constexpr ConstantClass(int i)
{
// Construction implementation
}
};
template<int i>
class ConstantTemplateClass
{
// Option 4
// - static assert on template parameter
// - Works but we'd prefer non templates in this case
static_assert(i>=0);
};
int main()
{
// Examples of invalid instances
// ConstantClass MyC = ConstantClass::ConstEvalFactory(-1);
// ConstantClass MyC = ConstantClass::TemplateConstEvalFactory<-1>();
// ConstantClass MyC = ConstantClass::ConstExprFactory(-1);
// ConstantTemplateClass<-1> MyC;
}