【问题标题】:Can I force a compiler error if certain functions are called?如果调用某些函数,我可以强制编译器错误吗?
【发布时间】:2012-05-30 10:37:20
【问题描述】:

我的软件有 v1 和 v2 版本。 v1 使用注册表来保存设置,对 GetProfileInt 等进行大量调用。v2 现在使用 sqlite db 来保存设置。

我们目前正在开发这两个分支并将新功能从 v1 合并到 v2 分支。我们目前必须记住更新任何注册表调用以使用新的配置数据库,这已经错过了几次。

如果在 v2 中使用了任何 GetProfile... 或 WriteProfile... 函数,我希望引发编译器错误。

我们在 Visual Studio 2010 中使用 C++。如果没有内置任何内容,我可以使用脚本的输出以某种方式引发编译器错误吗?

【问题讨论】:

  • 你可以尝试#define他们来处理无法编译的垃圾代码?
  • 您不能重新声明它们吗?
  • 内置是什么意思? GetProfile 等并不比 sqlite 的内置更多。
  • 补充@Mystical 的想法,#define GetProfile 到 dont_us_get_profile_use_this_other_thing_instead 之类的东西。
  • 我可能是一个尼安德特人,但我只是在一个测试用例中插入了一个 greps GetProfileInt 的来源...

标签: c++ visual-studio-2010 visual-c++ compiler-errors compiler-warnings


【解决方案1】:

既然这个答案被接受了,我不妨包括提问者实际使用的解决方案:

jacobsee 发现了deprecated pragma

#pragma deprecated(GetProfileInt)

原答案:

您可以使用__declspec(deprecated) 将它们声明为已弃用。它看起来像这样:

UINT __declspec(deprecated) WINAPI GetProfileInt(
  __in  LPCTSTR lpAppName,
  __in  LPCTSTR lpKeyName,
  __in  INT nDefault
);

您必须从包含在您关心的每个翻译单元中的标题中执行此操作。这样做会在任何时候包含已弃用声明的翻译单元使用该函数时发出警告。

如果您想要编译器错误并且您的项目尚未将警告视为错误,那么您必须打开它,并且可能修复您一直忽略的所有警告。 (无论您是否使用此解决方案,这些都是很好的做法。)

【讨论】:

  • 仅供参考,cl 有一个选项 /FI 可用于包含标题而无需修改任何源,可能在这里有用:/FImydeprecated.h
  • 或者,可以将函数声明放在匿名命名空间中,如果调用它,编译器会报告歧义。
  • 我试过你的代码,但它对我不起作用。我得到一个 dll 链接器错误。然后尝试将 WINBASEAPI 放在前面并编译。它想匹配 SDK 版本与我认为的 CWinApp 版本?但后来它无法检测到我对 AfxGetApp()->GetProfileInt 的使用。但我确实找到了一个很好的解决方案:只需将#pragma deprecated(GetProfileInt) 添加到全局头文件中。我对 GetProfileString、WriteProfileInt、WriteProfileString 做了同样的事情。谢谢!
  • 这里有一个链接让我找到了答案:weseetips.com/2008/12/22/…
  • 我还将项目配置 C/C++\Advanced\Treat Specific Warnings As Errors = 4995 以将 pragma deprecated 警告视为错误。我们还没有到将所有警告都视为错误的地步,而是朝着那个目标努力。
【解决方案2】:

将我的评论推广到答案:

您可以使用宏将它们重新定义为无法编译的内容:

#define GetProfile  HAHA_Nice_try_This_will_not_compile!!!

要注意的是,您需要确保它不会(合法地)在您的代码之外被调用。
(因此您应该将宏放在所有包含之后。)

【讨论】:

  • 宏通常是个坏主意,它会到处进行文本替换,即使有一个合法的 GetProfile 名称,比如作为成员函数。声明函数比声明宏要安全得多。
  • 好点。我忘了考虑成员函数。尽管除了 bames53 的回答之外,我想不出一种方法可以重新声明该函数并让它完全编译?
  • @Mystical:你可以使用匿名命名空间代替deprecated
  • @GeneBushuyev 哦……这很棘手! :) 我试图想一些类似于模棱两可的过载的东西。但是命名空间黑客更好!
【解决方案3】:

接受的答案是将函数标记为已弃用,但这并不真正符合问题的要求,原因有两个:

  • 它只给出警告,而不是错误。
  • 即使您使用的是 v1 代码,它也会发出警告。

有充分的理由想要这样做,但这实际上并不是最初的问题所要求的。

幸运的是,有一种非常简单的方法可以获取问题所要求的内容。如果函数根本不存在,编译器总是会抛出错误。只需将函数放入#ifndef

#ifndef V2

void GetProfile()
{
  // Get the profile
}

void WriteProfile()
{
  // Write the profile
}

#endif

【讨论】:

    【解决方案4】:

    如果您有一个常用的#include 文件,您可以在其中添加类似的内容(使用WA 视情况而定)。它至少会导致链接器错误(编译器警告/错误将取决于标志):

    #define GetProfileIntA InvalidFunctionDoNotCallMe
    

    Windows 已经将GetProfileInt 等函数定义为A 或W 版本的宏。

    【讨论】:

    • 您不需要常见的包含文件,只需使用 /FI 强制在所有 cpp 文件中包含文件。您可以在项目文件中设置 /FI。我在这里写了一篇博客:lenholgate.com/blog/2004/07/…
    • @LenHolgate:这是一个很好的信息。我不知道 /FI 开关。
    • @LenHolgate:/FI 不是在每个 TU 的 start 处包含文件吗?因此#define 也将在声明GetProfileIntA 的标头中生效。充其量你会得到一个链接器错误,最坏的情况是 GetProfileIntA 是一个内联函数,但一切仍然有效!
    • @Len Holgate - 我真的不喜欢在项目选项中隐藏包含文件的想法。过去,我在挖掘人们重新定义或隐藏的东西时遇到了很多麻烦。我宁愿要求在任何地方都包含标题,这是为可读性付出的小代价。
    • 基因,考虑到原始问题的要求,我认为值得一提。不过我同意你的看法,这不是我想长期使用的做法。不过它可能很有用。
    【解决方案5】:

    如果您可以随意将函数转换为模板,则可以执行以下操作:

    template <typename...>
    struct always_false { static constexpr bool value = false; };
    
    template <typename... Ts>
    void never_call_me(Ts&&...)
    {
      static_assert(always_false<Ts...>::value,
                    "You should have never called this function!");
    }
    

    这样做的好处是编译错误是干净的+您可以提供错误消息。 Found here,请参阅该答案以获取有关此功能为何有效以及为什么需要 always_false 的更多信息。

    【讨论】:

    • 这应该是公认的答案。如果“=delete”是类的成员函数,需要在调用时发出错误,也可以使用它。
    【解决方案6】:

    我相信你可以在#include'ing Windows.h 之后#define GetProfileInt(a, b, c) "don't use this";

    由于GetProfileInt 是一个用于路由到正确函数的宏,这将导致宏重新定义。而且由于char[]不能赋值给UINT,编译器error的。

    虽然这是一个肮脏,肮脏的黑客,但我想洗个澡来讨论它。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多