Search code examples
cstaticconstantsheader-filesconstant-expression

Cannot define constant variable that depends on another variable from a different header file in C


I have a set of C files that I'm trying to compile

//table.c
#include "row.h"

const uint32_t ROW_SIZE_1 = 50; //some random number
const uint32_t PAGE_SIZE = 4096;
const uint32_t ROWS_PER_PAGE = PAGE_SIZE / ROW_SIZE_1;
const uint32_t TABLE_MAX_ROWS = ROWS_PER_PAGE * TABLE_MAX_PAGES;
//row.c
#include "row.h"

const uint32_t ID_SIZE = size_of_attribute(Row, id);
const uint32_t USERNAME_SIZE = size_of_attribute(Row, username);
const uint32_t EMAIL_SIZE = size_of_attribute(Row, email);
const uint32_t ID_OFFSET = 0;
const uint32_t USERNAME_OFFSET = ID_OFFSET + ID_SIZE;
const uint32_t EMAIL_OFFSET = USERNAME_OFFSET + USERNAME_SIZE;
const uint32_t ROW_SIZE = ID_SIZE + USERNAME_SIZE + EMAIL_SIZE;

This compiles fine. But When I change 6th line in table.c to

const uint32_t ROWS_PER_PAGE = PAGE_SIZE / ROW_SIZE;

the compiler throws error: initializer element is not constant

my row.h file looks like this

//row.h

extern const uint32_t ID_SIZE;
extern const uint32_t USERNAME_SIZE;
extern const uint32_t EMAIL_SIZE;
extern const uint32_t ID_OFFSET;
extern const uint32_t USERNAME_OFFSET;
extern const uint32_t EMAIL_OFFSET;
extern const uint32_t ROW_SIZE;

The program compiles successfully when all the constants are defined in a single file. But when they are defined in multiple files, am getting the above error.

I also tried defining the constants in the row.h file but that led to multiple definitions error since it is imported in multiple areas

How do I resolve this or is there a workaround for this ?

Edit:

As gulpr and stark pointed out, redefining these constants as macros in the .h files worked

Working solution

//row.h

#define ID_SIZE_MACRO size_of_attribute(Row, id)
#define USERNAME_SIZE_MACRO size_of_attribute(Row, username)
#define EMAIL_SIZE_MACRO size_of_attribute(Row, email)
#define ID_OFFSET_MACRO 0
#define USERNAME_OFFSET_MACRO ID_OFFSET + ID_SIZE
#define EMAIL_OFFSET_MACRO USERNAME_OFFSET + USERNAME_SIZE
#define ROW_SIZE_MACRO ID_SIZE_MACRO + USERNAME_SIZE_MACRO + EMAIL_SIZE_MACRO
//row.c
#include "row.h"

const uint32_t ID_SIZE = ID_SIZE_MACRO;
const uint32_t USERNAME_SIZE = USERNAME_SIZE_MACRO;
const uint32_t EMAIL_SIZE = EMAIL_SIZE_MACRO;
const uint32_t ID_OFFSET = ID_OFFSET_MACRO;

const uint32_t USERNAME_OFFSET = USERNAME_OFFSET_MACRO;
const uint32_t EMAIL_OFFSET = EMAIL_OFFSET_MACRO;
const uint32_t ROW_SIZE = ROW_SIZE_MACRO;
// table.h
#define PAGE_SIZE_MACRO 4096
#define ROWS_PER_PAGE_MACRO PAGE_SIZE_MACRO / ROW_SIZE_MACRO
#define TABLE_MAX_ROWS_MACRO ROWS_PER_PAGE_MACRO * TABLE_MAX_PAGES
//table.c
#include "row.h"

const uint32_t PAGE_SIZE = PAGE_SIZE_MACRO;
const uint32_t ROWS_PER_PAGE = ROWS_PER_PAGE_MACRO;
const uint32_t TABLE_MAX_ROWS = TABLE_MAX_ROWS_MACRO;

Solution

  • A const variable in not a constant expression.

    When you have them in one file the compiler is treating them this way, but when you have them defined in another compilation unit it cannot "guess" the value of this expression during the compilation.

    You need to:

    #define ROW_SIZE_1  50 /* some random number*/
    #define PAGE_SIZE  4096
    

    in the .h file included in both source files.

    from C standard 6.6:

    An integer constant expression shall have integer type and shall only have operands that are integer constants, enumeration constants, character constants, sizeof expressions whose results are integer constants, and floating constants that are the immediate operands of casts. Cast operators in an integer constant expression shall only convert arithmetic types to integer types, except as part of an operand to the sizeof operator.

    and

    An arithmetic constant expression shall have arithmetic type and shall only have operands that are integer constants, floating constants, enumeration constants, character constants, and sizeof expressions. Cast operators in an arithmetic constant expression shall only convert arithmetic types to arithmetic types, except as part of an operand to a sizeof operator whose result is an integer constant.

    I can't define everything as macros since certain constant values are based on the size of structure objects and all the other constants depend on this macro. is there any other way to achieve this ?

    I do not know where you did get this knowledge from, but you can use definitions and it is 100% correct.

    #define size_of_attribute(Struct, Attribute) sizeof(((Struct*)0)->Attribute)
    #define ID_SIZE  size_of_attribute(Row, id)
    #define USERNAME_SIZE size_of_attribute(Row, username)
    #define EMAIL_SIZE size_of_attribute(Row, email)
    #define ID_OFFSET  0
    #define USERNAME_OFFSET (ID_OFFSET + ID_SIZE)
    #define EMAIL_OFFSET  (USERNAME_OFFSET + USERNAME_SIZE)