Cyber Axe Cyber Axe - 3 months ago 10
C Question

Why does Example A Compile and Example B Errors with "initializer element is not constant"

Edit:
I had made a few assumtions such as the static variable would be created and initialised once at runtime first time function was called.

I now know this is not the case and will now just define a static and return its pointer and have an init function thats called once at the start of the program, so is essentially the same as I was doing it when I was using globals but keeping it out of global scope.

Original:

I'm trying to avoid Global Variables as is generally suggested, but needing some static data that I can refer to in multiple functions without having to constantly pass an object to every single function (Though if this ultimatly doesn't work i may have to resort to that)

Another potential advantage is since most of the functions I'm using this for are to be called very often, only initializing once for data that should never change should minimise performance loss.

The method i've come up with is creating a static variable and initing it once, if i need to use it in a single function i'll init within the function directly and then use it, if i need it in multiple functions i'll init it in a seperate static function that the others can call for the pointer

Edit:
WINDOWS is a custom macro

#ifndef WINDOWS
#if defined(_WIN32) || defined(_WIN64)
#define WINDOWS
#endif
#endif


Also the compiler is MinGW 64bit

Example A Compiles Fine but Example B Throws an Error "initializer element is not constant"

Example A:

static ProgramPath getPath() {
ProgramPath result;
// Get Executable Path
#ifdef WINDOWS
GetModuleFileName(NULL, result.executablePath, sizeof(result.executablePath));
#endif

// Seperate Path and Executable into Seperate Variables
for (unsigned int i = 0; i < sizeof result.executablePath; ++i) {
// Convert \ to /
if (result.executablePath[i] == 0x5c) {
result.executablePath[i] = 0x2F;
} else if (result.executablePath[i] == 0x0) { // 0x0 means we've reached end of string.
unsigned int j;

// Get Executable Name Sub String
for (j = i - 1; j > 0; --j) {
if (result.executablePath[j] == 0x2F) { // Stop when we reach a /
result.executableStringLen = i - j;
memcpy(&result.executable, &result.executablePath[j + 1], result.executableStringLen); // Copy Executable SubString into correct variable
break;
}
}

result.executableStringLen = ++j;

// Remove Executable from Path
for (; j < i; ++j) {
result.executablePath[j] = 0x0;
}
break;
}
}

CA_VERBOSE_DEBUG("Executable: %s", executable);
CA_PRINT_DEBUG("Executable Path: %s", executablePath);

return result;
}

static inline ProgramPath *getPathInfo() {
static ProgramPath programPath = getPath();
return &programPath;
}


Example B:

static GraphicsInfo getGraphicsInfo() {
GraphicsInfo result;

result.internalPixelFormat = GL_BGRA;
result.internalPixelType = GL_UNSIGNED_INT_8_8_8_8_REV;

// Get Preferred Internal Pixel Format
if (glGetInternalformativ) {
const GLenum preferedInternalFormats[4] = {
GL_RGB,
GL_BGR,
GL_RGBA,
GL_BGRA
};

GLint test;
// Check for Internally Supported Formats and Use Best One
for (int i = 3; i >= 0; --i) {
// Check for Prefered
glGetInternalformativ(GL_RENDERBUFFER, preferedInternalFormats[i], GL_INTERNALFORMAT_PREFERRED, 1, &test);

if (test == GL_TRUE) {
result.internalPixelFormat = preferedInternalFormats[i];
break;
}
}
}

return result;
}

static inline GraphicsInfo *CA_getGraphicsInfo() {
static GraphicsInfo result = getGraphicsInfo(); // Error Occurs Here
return &result;
}


I have also tried commenting out the bulk of the code in Example B to no avail

Answer

You cannot initialize a variable with static storage from the return value of a function:

static inline GraphicsInfo *CA_getGraphicsInfo() {
    static GraphicsInfo result = getGraphicsInfo(); // Error Occurs Here
    return &result;
}

The 2 code fragments deal with different structures for which you have not provided the definition, neither should compile, but note that if WINDOWS is not defined, getPath manipulates an uninitialized structure result, so anything can happen, including the compiler playing tricks on you.

It is also possible that, since both functions are defined as static inline, the compiler only reports the error when it actually tries to expand getPath inline at a call site, and still reports the error with a the location at the definition of the inline function.

Furthermore, here are some problems with getPath:

  • it invokes memcpy on potentially overlapping objects, this also invokes undefined behavior: use memmove instead.

  • result.executableStringLen = ++j; is incorrect and should be removed. If the '/' was found, the length has already be updated, but you should initialize it before the search to result.executableStringLen = i;

  • If the '/' was not found, you effectively clear the array except for the very first byte. You should instead start from j = result.executableStringLen.

Comments