When we define a variable of a built-in type too large to parse, Compiler/IDE (I'm using Visual Studio 2019) warns right away that the range has been violated, even before compiling the project - "integer constant is too large" if we're defining an integer.
int sum = 100000000000000000000; // E0023, "integer constant is too large"
Is it possible to achieve similar type checking for user defined types in c++/VS (I'm not sure if this is purely IDE constrained, might be though - I'm new to c++)? Would be helpful to tell about going out of range in some cases, e.g.
MonthDay day = 32; // error, "MonthDay literal can't be greater than 31."
Weight applesWeight = 2900000_kg; // "Item too heavy." Yet better if it could also work with custom literals.
A version from constexpr constructor with compile time validation uses constexpr
, but that requires the keyword at variable declaration.
class A
{
public:
constexpr A(int i) : i(i != 42 ? throw 42 : i) {}
private:
int i;
};
// usage
constexpr A ab = 43; // error, "Expression must have a constant value"
constexpr A abc = 42; // ok
=== Outcome ===
As Lev.M. mentions in the comments this type of quick 'pre-compile-time' analyses are done with plug-ins (sometimes called a linter, and they're do more basic checks than other static analyzers - link below) and apparently VC++ 2019 comes with a new linter built-in. That's where native type overflows are checked as code is being typed, and it seems there's no way to extend it at this point for VC++ (has been possible for C# for some while now, if you're familiar with Visual Studio, Roslyn, etc.) I was wondering if the new VC++ linter could've been extended somehow, but looks like not currently. Marking @cigien's post as the answer.
https://devblogs.microsoft.com/cppblog/intellisense-code-linter-for-cpp/
You can add a check to your constructor for the argument being inside a range, and then assert on that:
struct MonthDay
{
constexpr MonthDay (int data) : data(data)
{
if (data > 31)
{
assert(false); // months can't have more than 31 days
}
}
private:
int data;
};
So long as the constructor is constexpr
this works fine with user-defined-literals:
constexpr MonthDay operator "" _md ( unsigned long long arg )
{
return MonthDay (arg);
}
and now you can get a nice error when the initialization fails:
MonthDay a = 15_md; // ok
MonthDay b = 32_md; // run-time error
This also gives a compile time error if initialized with an out-of-range value at compile-time, because of the assert
being evaluated in the if
clause of the constructor:
constexpr MonthDay c = 15_md; // ok
constexpr MonthDay d = 32_md; // compile time error
Here's a demo