Итак, я создаю небольшую тестовую библиотеку для некоторых простых задач. Я использую метод саморегистрации для определения тестов, но я получаю segfault, откуда я не понимаю. Мой проект выглядит так
Lib
|
|__include
| |__lib.hpp
|__src
| |__lib.cpp
|__examples
| |__example.cpp
|__Makefile
Это мой .hpp. Пожалуйста, игнорируйте общедоступные карты, так как я прекрасно понимаю, что они не должны быть общедоступными в финальной версии.
#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);
};
И это мой пример файла:
#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;
}
};
Это работало, когда я собирал все вместе вручную, но потом я решил создать статическую библиотеку и скомпилировать пример отдельно, он начал segfaulting. Вот как выглядит мой Makefile (как видите, я не профессиональный пользователь Makefile, пожалуйста, будьте осторожны)
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)/*
Изначально у меня были все статические методы и переменные, и я изменил их на синглтон, чтобы избежать проблем с инициализацией и памятью. Однако я получаю segfault в методе регистрации, когда он пытается получить доступ к тестовой карте. Любые идеи?
Обновлено: Итак, я воспроизвел компиляцию вручную так:
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
Полученный исполняемый файл запущен. Я собираюсь попытаться уловить любую разницу в процессе сейчас.
Я сделал. Сбой происходит в TestFactory::Register(), в 'auto it = Tests.find(name);' линия. Он не может получить доступ к тестовой карте.
Итак, после тщательного анализа кажется, что файл example.o не был успешно сгенерирован. Я в основном добавил их как требование для исполняемого рецепта и обновил чистый рецепт. Я считаю, что файл .a также может быть передан в качестве входных данных для окончательной команды g++.
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
Вероятно, вам нужно использовать отладчик, чтобы понять, почему. Хороший отладчик позволит вам остановиться там, где происходит сбой кода, и проверить переменные и стек вызовов вокруг сбоя.