Is there a __builtin_constant_p() for Visual C++?
Asked Answered
R

4

13

Is there some function like GCC's __builtin_constant_p() for Microsoft Visual Studio? As I understand, the function returns non-zero if the argument is constant, like a string literal.

In the answer here (How to have "constexpr and runtime" alias) is a nice use case of it.

EDIT: My idea was instead of writing something like:

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

I could write:

#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");
}

(I guess that is about what was written in the linked question.) All I need for that is a variant of __builtin_constant_p().

Romina answered 4/1, 2014 at 9:8 Comment(2)
Lol, u beat me to the question. 1up. (context: i posted the referenced question)Tacye
None of the answers are of any use for something like deciding between two ways to do something with vector intrinsics. (e.g. one that works well with immediate constants but worse or not at all with values that can't be encoded into shift / shuffle instructions or _mm_set1_epi8() vector constants). I guess that means MSVC doesn't have anything like this. Even clang's implementation is flaky (e.g. clang-3.8 fails to propagate it after inlining).Emma
S
1

It's possible to trick msvc into something very similar to GCC's __builtin_constant_p() using the MSVC-specific __if_exists (though only when compiling for c++, since it requires templates):

#ifdef _MSC_VER
extern "C++" {
namespace __intern {
template<bool> struct __msvc_constant_p {};
template<> struct __msvc_constant_p<true> { bool __is_constant_p__(); };
}
#define __builtin_constant_p(x) \
    (0 __if_exists(::__intern::__msvc_constant_p<!!(x) || !(x)>::__is_constant_p__){+1})
}
#endif /* _MSC_VER */

#define STATIC_ASSERT(x) static_assert(x, #x)

int foo(int a) {
    STATIC_ASSERT(__builtin_constant_p(0));
    STATIC_ASSERT(__builtin_constant_p(10));
    STATIC_ASSERT(__builtin_constant_p("foo"));
    STATIC_ASSERT(__builtin_constant_p("foo"[1]));
    STATIC_ASSERT(!__builtin_constant_p(a));
    return a;
}

Godbolt example: https://godbolt.org/z/MY5rWTjnK

However, this solution has the following problems:

  • Intellisence will add red underline to all instances where the expression passed to __builtin_constant_p() isn't actually constant (though note that this is a problem with intellisence only; the actual msvc compiler will be able to compile the code). Also note that despite this error-notice, intellisense will actually expand the expression to false
  • When arguments passed to __forceinline functions (in gcc: __attribute__((always_inline))) are constant, GCC's __builtin_constant_p() would return true, but the MSVC one above won't (it'll just keep returning false)
Suborn answered 30/6, 2024 at 16:51 Comment(1)
Oh: and to use this to implement an auto-constant strlen: #define STRLEN(x) (__builtin_constant_p(x) ? sizeof(x) - 1 : strlen(x))Suborn
D
-1

Here is an example about how to get compile-time detection of string length (which is not the answer to the initial question but to the second one)

Please notice however that most compiler already replace strlen("bob") by 3 in the very first optimization level, so I doubt it has any use in reality.

   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)

Proof that it works on a recent compiler:

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;
}

Some strange coding practice will not trigger compile-time behaviour like in constexpr const char * b = "bob";, this will call the run-time version because the type, at the time of call is const char* (constexpr is not a modifier you can select upon in a template, or I don't know how)

Dilapidation answered 21/8, 2015 at 15:39 Comment(0)
T
-5

In Visual Studio 2012 and Visual Studio 2013 there is the _IS_LITERAL_TYPE macro which makes use of std::is_literal_type, which is documented at http://www.cplusplus.com/reference/type_traits/is_literal_type/.

The following is a relevant excerpt from the documentation of is_literal_type.

"""Trait class that identifies whether T is a literal type.

A literal type is a type that can qualify as constexpr."""

Perhaps this would suffice.

The following excerpt from the documentation for __builtin_constant_p leads me to believe it will.

"You can use the built-in function __builtin_constant_p to determine if a value is known to be constant at compile-time..."

To me the phrases "is a literal type," "constexpr," and "known to be constant at compile-time" have the same meaning. Perhaps I am mistaken.

Then again, I will be the first to admit that I am not certain.

Teacher answered 13/2, 2014 at 0:29 Comment(8)
Thanks for your hint. Unfortunately, it doesn't help, since is_literal_type only says something about the type. E.g. std::is_literal_type<const char*>::value is true, but for my use it's all the difference if it's strlen(some_const_ptr) or strlen("abc"), even if both parameters are const char*.Romina
Perhaps my constant_p function would work. In my tests it does tell the difference between a string constant and a dynamically allocated string.Teacher
But I need that information at compile time, thus it cannot help me.Romina
@cxxl, I think you're mistaken. strlen(some_const_ptr) is const char*, but strlen("abc") is const char&[4]Dilapidation
@xryl669: thanks. does this change anything for my original question?Romina
@cxxl: Yes, a bit, since it allows a solution (see my answer). However, this solution is of very little interest as most compiler already pre-compute the string length at compile time if they can do so and replace by the computed value (easy optimization).Dilapidation
This answer completely misses the point of __builtin_constant_p. It's for something like int foo(int a) { if(__builtin_constant_p(a)) { some pure C++ that the compiler groks; } else { asm("inc %0" : "+r"(a); } return a; } Even without inline asm, you might have two implementations: one that compiles to code that works well at run-time but doesn't optimize away completely for compile-time constants, and another that's simpler and does optimize away.Emma
A literal type is one where __builtin_constant_p(x) can be true if x has that type. e.g. it just means that you can use constexpr with that type, not that the type can only be used to hold compile-time constants. e.g. int is a literal type, as the example you linked on cplusplus.com says. I think you got confused talking about strings, since working out whether the pointed-to string is a compile-time constant is not exactly the same thing from working out whether an address is a compile-time constant. (Addresses are usually link-time constants anyway, not compile-time...)Emma
T
-6

If is_literal_type is not what you want, the following function might be of use. With it I was able to tell the difference between a char string that was defined as follows and one that was allocated on the heap.

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

My implementation of constant_p is as follows.

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;
}

Note you need to include DbgHelp.h and link with DbgHelp.lib in order for this to work.

I hope one of my proposed solutions works for you. I would like to know.

Teacher answered 13/2, 2014 at 2:12 Comment(1)
This looks like a atomic head to kill a mosquito. Yes it probably work, but it's completely run-time based, not compile-time based.Dilapidation

© 2022 - 2025 — McMap. All rights reserved.