【问题标题】:Custom type range/value check compile-time, real-time自定义类型范围/值检查编译时,实时
【发布时间】:2021-05-23 11:35:14
【问题描述】:

当我们定义一个太大而无法解析的内置类型的变量时,编译器/IDE(我使用的是 Visual Studio 2019)会立即警告该范围已被违反,甚至在编译项目之前 - “整数常量太大了”,如果我们定义一个整数。

int sum = 100000000000000000000; // E0023, "integer constant is too large"

是否可以在 c++/VS 中为用户定义的类型实现类似的类型检查(我不确定这是否纯粹是 IDE 限制,但可能是 - 我是 c++ 新手)? 在某些情况下告诉我们超出范围会很有帮助,例如

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.

constexpr constructor with compile time validation 的版本使用constexpr,但这需要在变量声明处使用关键字。

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

=== 结果 ===

作为 Lev.M.在 cmets 中提到这种类型的快速“预编译时”分析是通过插件完成的(有时称为 linter,它们比其他静态分析器进行更多基本检查 - 链接如下) 显然 VC++ 2019 内置了一个新的 linter。这就是在输入代码时检查本机类型溢出的地方,似乎目前无法为 VC++ 扩展它(如果您熟悉 Visual Studio、Roslyn 等,C# 已经有一段时间了.) 我想知道新的 VC++ linter 是否可以以某种方式扩展,但目前看起来还没有。 将@cigien 的帖子标记为答案。

https://devblogs.microsoft.com/cppblog/intellisense-code-linter-for-cpp/

【问题讨论】:

  • 您在编译代码之前收到的任何警告或错误都来自您的 IDE。通常,当您更改文件时,IDE 会运行一些 linter,以帮助提前找到一些语法问题。不幸的是,我不知道是否可以扩展 VS C++ linter 来满足您的要求。

标签: c++ visual-c++ linter


【解决方案1】:

你可以在你的构造函数中添加一个检查参数是否在一个范围内,然后断言:

struct MonthDay
{
  constexpr MonthDay (int data) : data(data)
  {
    if (data > 31)
    {
        assert(false); // months can't have more than 31 days
    }  
  }

 private:
  int data;
};

只要构造函数是constexpr,这对用户定义的文字就可以正常工作:

constexpr MonthDay operator "" _md ( unsigned long long arg )
{ 
  return MonthDay (arg);
}

现在你可以在初始化失败时得到一个很好的错误:

MonthDay a = 15_md;  // ok
MonthDay b = 32_md;  // run-time error

如果在编译时使用超出范围的值进行初始化,这也会产生编译时错误,因为 assert 在构造函数的 if 子句中被评估:

constexpr MonthDay c = 15_md; // ok
constexpr MonthDay d = 32_md; // compile time error

这是demo

【讨论】:

  • 确实感谢@cigien。那么有可能constexpr 是唯一的方法吗?想一想,一种尴尬的解决方法是使用宏:#define MONTH_DAY constexpr MonthDay,然后是MONTH_DAY m = 32_md; // error right away。另外,您知道是否有可能引发更多信息错误?它说表达式没有计算为常量,而我的后编译器的 int 溢出说常量太大。可能不会,因为这是编译器错误。
  • @Arman 我不确定constexpr 是唯一的方法是什么意思。 user-defined-literal 函数必须是 constexpr(毕竟它们是 literals),但如果您只想在运行时执行操作,则在任何地方都不需要 constexpr -时间。至于错误消息,您可以在assert 中添加一个,这应该会提供更好的消息。也请不要使用宏;它们通常不会增加价值,在这种情况下也不会。
  • 如果您查看我的问题帖子,还有一个使用 w/o user-def-literals 仍然使用 constexpr,constexpr A ab = 32;//error 如果您删除 constexpr,错误就会消失。但看起来 constexpr 是运行编译时检查的唯一方法。我想知道对于没有额外关键字的用户数据类型,是否有任何编译时替代 smt 的方法,例如 int x = <large num>; //error - 但可能没有。
  • @Arman 不,不是。 constexpr 是说应该在编译时初始化和检查变量的方式。否则它们都是运行时检查。
猜你喜欢
  • 1970-01-01
  • 2020-02-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多