2

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/

Arman
  • 5,136
  • 3
  • 34
  • 36
  • 1
    Any warnings or errors you get before compiling your code come from your IDE. Usually IDE's run some linter when you change the file to help find some syntax prbolems in advance. Unfortunately, I have no idea if VS C++ linter can be extended to do what you are asking. – Lev M. Feb 20 '21 at 18:39

1 Answers1

1

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

cigien
  • 57,834
  • 11
  • 73
  • 112
  • Thanks indeed @cigien. So chances are `constexpr` is the only way? One awkward workaround would be, just thought, to use macros: `#define MONTH_DAY constexpr MonthDay` and then `MONTH_DAY m = 32_md; // error right away`. Also, do you know if it's possible to fire a more informative error? It says expression did not evaluate to a constant, while for the int overflow from my post compiler says Constant too big. Likely not, since this a compiler error. – Arman Feb 20 '21 at 20:36
  • 1
    @Arman I'm not sure what you mean by `constexpr` being the only way. The user-defined-literal function is required to be `constexpr` (they're *literals* after all), but otherwise the `constexpr` is not necessary anywhere if you just want to do things at run-time. As for the error message, you can add one to the `assert` and that should give a better message. Also please don't use Macros; they don't generally add value, and they won't in this case either. – cigien Feb 20 '21 at 20:51
  • If you look at my question post, there's a usage w/o user-def-literals still with constexpr, `constexpr A ab = 32;//error` The error goes away if you remove constexpr. But it looks like constexpr is the only way to run compile-time checks. I was wondering if there was any compile-time alternative to smt like `int x = ; //error` for user data types without extra keywords - but likely not. – Arman Feb 20 '21 at 21:06
  • @Arman No, not really. `constexpr` is the way to say that variables should be initialized and checked at compile time. Otherwise they're all run-time checks. – cigien Feb 20 '21 at 21:14