Visual C++有 __builtin_constant_p() 吗?

Is there a __builtin_constant_p() for Visual C++?

提问人:cxxl 提问时间:1/4/2014 最后编辑:Communitycxxl 更新时间:8/21/2015 访问量:2829

问:

Microsoft Visual Studio 是否有类似 GCC 的功能?据我了解,如果参数是常量,则该函数返回非零,就像字符串文字一样。__builtin_constant_p()

在这里的答案(如何拥有“constexpr 和运行时”别名)是一个很好的用例。

编辑:我的想法是,而不是写这样的东西:

#include <string.h>
int foo() {
   return strlen("text");
}

我可以写:

#include <string.h>
// template_strlen() would be a function that gets the length of a compile-time     const string via templates
#define STRLEN(a) (__builtin_constant_p(a) ? template_strlen(a) : strlen(a))
int foo() {
   return STRLEN("text");
}

(我想这与链接问题中所写的内容有关。 我所需要的只是 的变体。__builtin_constant_p()

C++ :Windows 工作室 :可视化 C++

评论

0赞 PicoCreator 1/5/2014
哈哈,你打败了我这个问题。(上下文:我发布了参考问题)
1赞 Peter Cordes 6/11/2016
对于在两种方法之间做出决定来使用向量内部函数做某事之类的事情,没有一个答案有任何用处。(例如,一个适用于即时常量,但更差或根本不适用于无法编码为移位/随机指令或向量常量的值)。我想这意味着 MSVC 没有这样的东西。甚至 clang 的实现也是不稳定的(例如,clang-3.8 在内联后无法传播它)。_mm_set1_epi8()

答:

-5赞 Benilda Key 2/13/2014 #1

在 Visual Studio 2012 和 Visual Studio 2013 中,有一个使用 std::is_literal_type 的_IS_LITERAL_TYPE宏,该宏记录在 http://www.cplusplus.com/reference/type_traits/is_literal_type/ 中。

以下是is_literal_type文档的相关摘录。

“”“标识 T 是否为文本类型的 Trait 类。

文本类型是可以限定为 constexpr 的类型。

也许这就足够了。

以下摘录自 __builtin_constant_p 文档,让我相信它会的。

“您可以使用内置函数 __builtin_constant_p 来确定某个值在编译时是否已知为常量...”

对我来说,短语“is a literal type”、“constexpr”和“known to be constant-time”具有相同的含义。也许我错了。

话又说回来,我将是第一个承认我不确定的人。

评论

1赞 cxxl 2/14/2014
谢谢你的提示。不幸的是,它没有帮助,因为只说明了有关类型的一些信息。例如 是 ,但就我而言,如果它是 或 ,即使两个参数都是 ,也完全不同。is_literal_typestd::is_literal_type<const char*>::valuetruestrlen(some_const_ptr)strlen("abc")const char*
0赞 Benilda Key 2/14/2014
也许我的constant_p功能会起作用。在我的测试中,它确实区分了字符串常量和动态分配的字符串。
3赞 cxxl 2/14/2014
但是我在编译时需要这些信息,因此它对我无能为力。
0赞 xryl669 8/21/2015
@cxxl,我想你错了。 是 const char*,但strlen(some_const_ptr)strlen("abc")const char&[4]
0赞 cxxl 8/21/2015
@xryl669:谢谢。这对我原来的问题有什么改变吗?
-6赞 Benilda Key 2/13/2014 #2

如果is_literal_type不是您想要的,则以下函数可能有用。有了它,我能够分辨出定义如下的 char 字符串和在堆上分配的字符串之间的区别。

LPCTSTR constString = _T("Hello World!");

我对constant_p的实现如下。

int constant_p(const void *p)
{
    static bool s_init = false;
    static ULONGLONG s_TextSegmentStartVirtualAddress = 0;
    static ULONGLONG s_TextSegmentEndVirtualAddress = 0;
    static ULONGLONG s_RDataSegmentStartVirtualAddress = 0;
    static ULONGLONG s_RDataSegmentEndVirtualAddress = 0;
    if (! s_init)
    {
        s_init = true;
        PIMAGE_NT_HEADERS pNtHeaders = ::ImageNtHeader(
            reinterpret_cast<PVOID>(::GetModuleHandle(NULL)));
        if (! pNtHeaders)
        {
            return 0;
        }
        ULONGLONG ImageBase = pNtHeaders->OptionalHeader.ImageBase;
        PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)(pNtHeaders + 1);
        for (WORD i = 0; i < pNtHeaders->FileHeader.NumberOfSections; ++i)
        {
            char *name = (char*)pSectionHeader->Name;
            if (0 == ::strcmp(name, ".text"))
            {
                s_TextSegmentStartVirtualAddress = ImageBase
                    + pSectionHeader->VirtualAddress;
                s_TextSegmentEndVirtualAddress = s_TextSegmentStartVirtualAddress
                    + pSectionHeader->SizeOfRawData;
            }
            else if (0 == ::strcmp(name, ".rdata"))
            {
                s_RDataSegmentStartVirtualAddress = ImageBase
                    + pSectionHeader->VirtualAddress;
                s_RDataSegmentEndVirtualAddress = s_RDataSegmentStartVirtualAddress
                    + pSectionHeader->SizeOfRawData;
            }
            pSectionHeader++;
        }
    }
    if (0 == s_TextSegmentStartVirtualAddress)
    {
        // Something went wrong. Give up.
        return 0;
    }
    ULONGLONG test = reinterpret_cast<ULONGLONG>(p);
    if (
        s_TextSegmentStartVirtualAddress <= test
        && test <= s_TextSegmentEndVirtualAddress
    )
    {
        return 1;
    }
    else if (
         s_RDataSegmentStartVirtualAddress <= test
         && test <= s_RDataSegmentEndVirtualAddress
     )
    {
        return 1;
    }
    return 0;
}

请注意,您需要包含 DbgHelp.h 并与 DbgHelp.lib 链接才能正常工作。

我希望我提出的解决方案之一对您有用。我想知道。

评论

3赞 xryl669 8/21/2015
这看起来像一个杀死蚊子的原子头。是的,它可能有效,但它完全基于运行时,而不是基于编译时。
-1赞 xryl669 8/21/2015 #3

下面是一个关于如何获得字符串长度的编译时检测的示例(这不是第一个问题的答案,而是第二个问题的答案)

但是请注意,大多数编译器在第一个优化级别中已经被 3 替换,因此我怀疑它在现实中是否有任何用处。strlen("bob")

   template <typename T>
   struct StrLenHelper
   {
       static constexpr size_t len(T) { return 0; }
   };

   template <size_t sel>
   struct StrLenHelper<const char (&)[sel]>
   {
       static constexpr size_t len(const char (&a)[sel]) { return sel-1; }
   };


   template <>
   struct StrLenHelper<const char*>
   {
       static size_t len(const char * a) { return strlen(a); }
   };

   #define StrLen(X) StrLenHelper<decltype(X)>::len(X)

证明它适用于最近的编译器:

template <size_t A> 
struct Test { enum T { value = A }; };

// Outputs "5 5 4" if your program is called "test"
int main(int a, char**b)
{
   printf("%u %u %u\n", Test<StrLen("bobby")>::value, StrLen("bobby"), StrLen(b[0])); 
   return 0;
}

一些奇怪的编码实践不会触发编译时行为,例如 ,这将调用运行时版本,因为在调用时类型是(不是您可以在模板中选择的修饰符,或者我不知道如何)constexpr const char * b = "bob";const char*constexpr