Search code examples
ccompilationlinker

Stateful libraries in C/C++


I'm posting this question because I feel I do not understand some fundamental concept about C/C++ libraries.

Suppose we have a library that holds some state (static global variables, for example) named lib_c. Suppose there is another library lib_b that uses lib_c.

Now, I am writing a program A that needs both lib_b and lib_c. Is there any way that the lib_c that A uses will be different (i.e. get a separate copy of the static global variables) from the lib_c that lib_b uses?

To better illustrate my frustration, see the following code example.

Here is the "stateful library", it just has 2 static globals:

c.h:

#ifndef C_H
#define C_H
void init_variables(int a, int b);
int get_a();
int get_b();
#endif

c.c:

#include "c.h"
static int a_ = 0, b_ = 0;
void init_variables(int a, int b) {
    a_ = a;
    b_ = b;
}
int get_a() { return a_; }
int get_b() { return b_; }

Here is the other library that uses the stateful library:

b.h:

#ifndef B_H
#define B_H
void read_b();
#endif

b.c:

#include <stdio.h>
#include "b.h"
#include "c.h"
void read_b() {
    printf("As seen from b.c: %d, %d\n", get_a(), get_b());
}

And here is my code, that uses both libraries:

a.c:

#include <stdio.h>
#include "b.h"
#include "c.h"

void read_a() {
    printf("As seen from a.c: %d, %d\n", get_a(), get_b());
}

int main() {
    init_variables(1, 2);
    read_a();
    read_b();
    return 0;
}

Both read_a() and read_b() read the same contents of the static globals, 1 and 2! It appears that lib_b and my code do not get different copies of these static globals.

I've tried both compiling with plain gcc commands and building with CMake. Take a look at the CMakeLists I've written:

cmake_minimum_required(VERSION 3.22)

project(UnderstandStatefulLibs VERSION 1.0 LANGUAGES C)

# c.c -> lib_c
add_library(lib_c c.c)

# b.c + lib_c -> lib_b
add_library(lib_b b.c)
target_link_libraries(lib_b PRIVATE lib_c)

# a.c + lib_b + lib_c -> executable
add_executable(main a.c)
target_link_libraries(main PRIVATE lib_b lib_c)

Any clarification regarding this subject is welcome. I would like to know if it is at all possible to get separate copies of the static globals, and if so, how.

Thank you a lot!


Solution

  • Typically libraries that require some state to be stored internally are implemented so that there is an "open_library()" call that allocates a new copy of a data structure or class containing the state and returns an opaque handle (e.g. a void pointer to the structure).

    In all other calls to the library, the user must then provide the handle as the first parameter to the function.

    There should also be a "close_library(handle) call that deallocates the state structure allocated in "open_library()".

    The user does not see the internals of the state structure and can only manipulate the data contained in the it by calling functions in the library and passing the handle.

    As pointed out by OldBoy, libraries should avoid using global/static variables for state storage as that leads to problems when the library is used in multiple contexts or threads.