【问题标题】:C++11 Function That Only Accepts String Literals?仅接受字符串文字的 C++11 函数?
【发布时间】:2012-11-23 08:07:22
【问题描述】:

我想写一个只接受字符串字面量作为参数的 C++11 函数:

 void f(const char* s) { static_assert(s is a string literal); ... }

即:

 f("foo"); // OK

 char c = ...;
 f(&c); // ERROR: Doesn't compile

 string s = ...;
 f(s.c_str()); // ERROR: Doesn't compile

 etc

有没有办法实现这个?该函数的签名可以更改,添加宏或任何其他语言功能的使用也是如此。

如果这不可能,那么最接近的近似值是多少? (无论如何,用户定义的文字可以提供帮助吗?)

如果没有,GCC 4.7 / Linux 中是否有特定于平台的方式?

【问题讨论】:

  • 这是不可能的,因为字符串文字只是 char const[N] 数组,因此与它们无法区分。
  • @AndrewTomazos-Fathomling 即使您确实传递了字符串文字,也无法保证。示例:#include <stdio.h> / void f(const char *s) { printf(s); } / int main() { f("Hello, world!\n"); } 只会导致 "Hello, world!"(没有 '\n')在我的系统上的快速测试中出现在可执行文件中。
  • @AndrewTomazos-Fathomling 我的可执行文件中出现的不是我的字符串文字。这是我的字符串文字的一些位(在这种情况下是大多数位),但不是全部。我可以将f 更改为什么都不做,忽略它的参数,然后可执行文件中不会出现任何位。
  • @AndrewTomazos-Fathomling:关于 printf 的警告主要在 GCC 编译器内部实现(部分在 Glibc 头文件中通过适当的 __attribute__ 注释实现)。所以你需要扩展 GCC 来做类似的检查。看我的回答。
  • @AndrewTomazos-Fathomling 因此,您实际上并不关心字符串文字是否出现在应用程序图像中。我的 cmets 基于此,我的第二条评论是对您的“目标是强制在调用点将字符串逐字输入代码,并静态编译到应用程序映像中”的回复。在这种情况下,我将重新表述我的第一个问题:其他情况如何,即不直接传递字符串文字,但也不在运行时计算字符串?指向字符串文字的指针/引用、简单的const char array[] 等?

标签: c++ linux gcc c++11


【解决方案1】:

我用这个:

// these are used to force constant, literal strings in sqfish binding names
// which allows to store/copy just the pointer without having to manage
// allocations and memory copies
struct _literalstring
{
    // these functions are just for easy usage... not needed
    // the struct can be empty
    bool equal(_literalstring const *other) { return !strcmp((const char *)this, (const char *)other); }
    bool equal(const char *other) { return !strcmp((const char *)this, other); }
    const char *str(void) { return (const char *)this; }
    bool empty(void) { return *(const char *)this == 0; }
};

typedef _literalstring *LITSTR;

constexpr LITSTR operator "" _LIT(const char *s, size_t) {
    return (LITSTR)s;
}

然后你只需像这样声明你的函数:

void myFunc(LITSTR str)
{
    printf("%s\n", str->str());
    printf("%s\n", (const char *)str);
    const char *aVar = str->str();
    const char *another = (const char *)str;
}

你这样称呼它:

myFunc("some text"_LIT);

如果你这样做:

myFunc("some text");
myFunc(aTextVariable);

你得到一个编译器错误。

【讨论】:

    【解决方案2】:

    另一种方法可能是制作一个 GCC 扩展,以在编译时检查您的特定函数是否仅使用文字字符串调用。

    您可以使用MELT 来扩展 GCC。 MELT 是一种用于扩展 GCC 编译器的高级域特定语言,非常适合您想要的那种检查。

    基本上,你会在 GCC 中添加一个新的 pass 和在 MELT 中传递的代码,它会找到每个 gimple,它是对你的函数的调用,并检查参数是否确实是一个文字字符串。 melt-examples 上的 ex06 示例应该会激发您的灵感。然后订阅gcc-melt@googlegroups.com 并在那里询问您的 MELT 特定问题。

    当然,这不是万无一失的方法:函数可以通过指针间接调用,例如有一个部分文字字符串,例如f("hello world I am here"+(i%4)) 在概念上是一个带有一些文字字符串的调用(例如在.rodata 段中),但不在生成的代码中,也不在 gimple 中。

    【讨论】:

      【解决方案3】:

      我认为你会得到最接近的是这个

      template<int N>
      void f(const char (&str)[N]){
        ...
      }
      

      它将使用文字和数组而不是指针进行编译。

      【讨论】:

      • 您可以通过巧妙地使用std::enable_if&lt;&gt; 进一步阻止它与大多数非文字数组一起工作。请参阅 stackoverflow.com/questions/14805011/… 以了解在 enable_if 中添加内容的示例(尽管该问题使用 static_assert,但它是相同的概念)。
      • 本能地,我会使用 std::size_t 而不是 int 作为模板参数,但我找不到可以说使用其中一个或另一个的参考。如果有人有一些见解......
      猜你喜欢
      • 2011-01-03
      • 1970-01-01
      • 2013-05-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多