Initialisation in Singleton

Viewed 58

So, I am creating a small testing library for some simple tasks. I use the self-registration method to define the tests, but I am getting a segfault that I don't understand where is coming from. My project looks like this

Lib
|
|__include
|  |__lib.hpp
|__src
|  |__lib.cpp
|__examples
|  |__example.cpp
|__Makefile

This is my .hpp. Please, ignore the public maps, as I am well aware that they should not be public in the final version.

#include <string>
#include <map>
#include <functional>


#define TEST(name, result_var_name, short_result_var_name, explanation_var) \
    bool test_##name(std::string result_var_name); \
    static bool test_##name##_registered = TestFactory::getInstance()->Register(#name, &test_##name, #result_var_name, #short_result_var_name); \
    static bool test_##name##_description = TestFactory::getInstance()->RegisterExplanation(#result_var_name, explanation_var); \
    bool test_##name(std::string result_var_name)

#define RESULT_LONG(result_var_name, result) \
    TestFactory::getInstance()->RegisterResult(result_var_name, result);

#define RESULT_SHORT(result_var_name, result) \
    TestFactory::getInstance()->RegisterResult(TestFactory::getInstance()->short_to_long_param[result_var_name], result);


class TestFactory {
private:
    TestFactory();
public:
    std::map<std::string, std::function<bool(std::string)>> Tests;
    std::map<std::string, std::string> short_to_long_param;
    std::map<std::string, std::string> param_to_testName;
    std::map<std::string, std::string> testName_to_result;
    std::map<std::string, std::string> param_to_explanation;

    static TestFactory* getInstance();
    bool Register(std::string name, std::function<bool(std::string)> func, std::string long_param, std::string short_param);
    bool RegisterResult(std::string result_var_name, std::string result);
    bool RegisterExplanation(std::string result_var_name, std::string explanation);
};

And this is my example file:

#include "lib.hpp"
#include <iostream>    
TEST(testing_test, foo, f, "This is a description of foo"){
        try
        {
                return foo == "example";
        }
        catch(const std::exception& e)
        {
                std::cerr << "Exception thrown in testing_test: " << e.what() << '\n';
                return false;
        }
};

It was working when I built everything together manually, but then I decided to create the static library and compile the example separately, it started segfaulting. This is what my Makefile looks like (as you can see, I am not a pro Makefile user, please be gentle)

LIBNAME     := libtest.a

CXX       := g++

BIN     := bin
SRC     := src
INCLUDE     := include
LIB     := lib

LIBRARIES   := -ltest
EXECUTABLE  := example

LIBRARY_SOURCES := $(SRC)/lib.o
EXECUTABLE_SOURCES := examples/example.o

CXXFLAGS := -Wall -Wextra -std=c++17 -ggdb -I$(INCLUDE)

all: $(BIN)/$(EXECUTABLE)

run: clean all
    clear
    ./$(BIN)/$(EXECUTABLE)

$(LIB)/$(LIBNAME): $(LIBRARY_SOURCES)
    ar rcs $@ $^
        
$(BIN)/$(EXECUTABLE): $(LIB)/$(LIBNAME)
    $(CXX) $(CXXFLAGS) -I$(INCLUDE) -L$(LIB) $(EXECUTABLE_SOURCES) -o $@ $(LIBRARIES)
clean:
    -rm -rf $(BIN)/*
    -rm -rf $(LIB)/*

Initially, I had all static methods and variables, and I changed them to a singleton to avoid initialisation and memory issues. However, I am getting a segfault in the Register method when it is trying to access the Test map. Any ideas?

EDIT: So, I reproduced the compilation manually like so:

g++ -Iinclude -std=c++17 -c src/mxIntegration.cpp  -o lib/manual_object_file.o
ar rvs lib/manual_library.a lib/manual_object_file.o
g++ -Iinclude examples/main.cpp lib/manual_object_file.o -o bin/manual_test_linking

The resulting executable is running. I'm going to try to catch any difference in the process now.

1 Answers

So, after careful analysis, it seems like the example.o file was not being generated successfully. I basically added them as a requirement for the executable recipe, and updated the clean recipe. I believe the .a file could also be passed as an input to the final g++ command.

LIBNAME     := libIntegration.a

CXX       := g++

BIN     := bin
SRC     := src
INCLUDE     := include
LIB     := lib

LIBRARIES   := -l:$(LIBNAME)
EXECUTABLE  := main

LIBRARY_SOURCES := $(SRC)/mxIntegration.o
EXECUTABLE_SOURCES := examples/main.o

CXXFLAGS := -Wall -Wextra -std=c++17 -ggdb -I$(INCLUDE)

all: $(BIN)/$(EXECUTABLE)

run: clean all
    clear
    ./$(BIN)/$(EXECUTABLE)

$(LIB)/$(LIBNAME): $(LIBRARY_SOURCES)
    ar rvs $@ $^
        
$(BIN)/$(EXECUTABLE): $(LIB)/$(LIBNAME) $(EXECUTABLE_SOURCES)
    $(CXX) $(CXXFLAGS) -L$(LIB) $(EXECUTABLE_SOURCES) -o $@ $(LIBRARIES)
clean:
    -rm -rf $(BIN)/*
    -rm -rf $(LIB)/*
    -rm -rf **/*.o
Related