We have an assertion implementation something like this:
#define RetailAssert(expr) \
__declspec(allocate(".rdata$cold")) static const char exp = #expr; \
if (!expr) {fprintf(stderr, "%s(%d): assertion failed:%s\n", __FILE__, __LINE__, exp); abort(); }
__FILE__ and the format string should be similarly cold too.
I would like to make it an expression, like standard assert, something like this:
#define RetailAssert(expr) \
((expr) ? 0 : fprintf(stderr, "%s(%d): assertion failed:%s\n", __FILE__, __LINE__, __declspec(allocate(".rdata$cold"))(#expr)), abort()))
But __declspec(allocate) seemingly cannot be used with string literals.
This seems like a language hole. String literals are their own special thing.
There is #pragma const_seg, and __pragma(const_seg).
They do not quite work because they operate at function level.
Like, the last __pragma applies to its entire enclosing function.
Lately I have found that lambdas are very good at breaking things into functions, perhaps unnaturally, where you don't really intend to have a function, but just chunks of code, in order to gain functionality only offered to functions (such as declspec(noinline)).
So here:
// Place a string literal in a section.
// For example, for hot/cold split of assertion messages in retail builds.
// const_seg and data_seg for /GF and not
// 4177 pragma 'data_seg' should only be used at global scope or namespace scope
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#undef NDEBUG
#include <assert.h>
#pragma section("1", read)
#pragma section("2", read)
#pragma section("3", read)
#define SEGSTR(seg, str) \
([] { return \
__pragma(warning(suppress:4177)) \
__pragma(const_seg(push, seg)) \
__pragma(data_seg(push, seg)) \
str; \
} \
__pragma(const_seg(pop)) \
__pragma(data_seg(pop)) ()) \
extern "C" { extern IMAGE_DOS_HEADER __ImageBase; }
static PCH segNameOf(const void* p)
{
PIMAGE_DOS_HEADER dos = &__ImageBase;
PCH base = (PCH)dos;
PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)(dos->e_lfanew + (PCH)dos);
PIMAGE_SECTION_HEADER sec = IMAGE_FIRST_SECTION(nt);
size_t nsec = nt->FileHeader.NumberOfSections;
size_t i = 0;
for (i = 0; i < nsec; ++i)
{
if (p >= (base + sec->VirtualAddress) && p < (base + sec->VirtualAddress + sec->Misc.VirtualSize))
{
//printf("vprot %X\n", sec->Characteristics);
return (PCH)sec->Name;
}
++sec;
}
return "";
}
__declspec(noinline)
void assert_failed(const char* expression, const char* file, int line)
{
const char* a = SEGSTR("3", "%s(%d): assertion failed %s\n");
printf("default:%s 1:%s 2:%s 3:%s\n", segNameOf(""), segNameOf(expression), segNameOf(file), segNameOf(a));
fprintf(stderr, a, file, line, expression);
//abort();
}
#define Assert(exp) ((exp) ? ((void)0) : assert_failed(SEGSTR("1", #exp), SEGSTR("2", __FILE__), __LINE__))
int main(int argc, char** argv)
{
Assert(argc==1);
printf("default:%s\n", 1+segNameOf(""));
printf("text:%s\n", 1+segNameOf(&main));
static const char rdata[]="";
static char data[]="";
printf("rdata:%s\n", 1+segNameOf(&rdata));
printf("data:%s\n", 1+segNameOf(&data));
assert(!strcmp("rdata", 1+segNameOf(&rdata)));
assert(!strcmp("data", 1+segNameOf(&data)));
assert(!strcmp("text", 1+segNameOf(&main)));
}