我正在尝试利用构造未使用变量的副作用,因此我不希望它被优化。
好的,我知道在构造变量时使用副作用可能不是一个好的做法。那么请允许我解释一下目前的情况。
我有一个抽象类和一个具体类。我已经实现了一个工厂,它生成指向具体类的
Abstract*
指针。对于任何希望由抽象类产生的具体类,必须首先注册它们。这是一个最小的(非)工作示例:
// Abstract.hpp
#pragma once
class Abstract {
public:
virtual void Hello() const = 0;
};
// Abstract.cc
#include "Abstract.hpp"
#include "Factory.hpp"
template<>
Factory<Abstract>* Factory<Abstract>::_the_factory = nullptr;
// Concrete.hpp
#include "Abstract.hpp"
#include "iostream"
class Concrete : public Abstract {
public:
void Hello() const override {
std::cerr << "B" << std::endl;
}
};
// Concrete.cc
#include "Concrete.hpp"
#include "Factory.hpp"
const bool concrete_registered = Factory<Abstract>::GetInstance()->Register("concrete", []() {
return new Concrete;
});
// Factory.hpp
#pragma once
#include <map>
#include <string>
#include <functional>
#include <iostream>
template <class Product>
class Factory {
public:
using ProductCreator = std::function<Product*()>;
static Factory* GetInstance() {
if (!_the_factory) {
_the_factory = new Factory;
}
return _the_factory;
}
bool Register(const std::string& name, const ProductCreator& creator) {
bool result = _creator_map.insert(std::make_pair(name, creator)).second;
if (result) {
std::cerr << name << " registered" << std::endl;
} else {
std::cerr << name << " can't be registered" << std::endl;
}
return result;
}
Product* GetProduct(const std::string& name) {
if (_creator_map.find(name) == _creator_map.end()) {
std::cerr << "Unknown product name " << name << " detected";
}
return _creator_map[name]();
}
private:
static Factory* _the_factory;
std::map<std::string, ProductCreator> _creator_map;
};
我希望通过将
concrete_registered
设置为常量,我可以强制程序在main
之前执行注册代码,以便我可以从工厂获得Concrete
的产品。以下是我如何使用它们:
// main.cc
#include "Abstract.hpp"
#include "Factory.hpp"
int main() {
auto ptr = Factory<Abstract>::GetInstance()->GetProduct("concrete");
ptr->Hello();
}
整个项目的结构如下:
├── CMakeLists.txt
├── lib
│ ├── Abstract.cc
│ ├── Abstract.hpp
│ ├── CMakeLists.txt
│ ├── Concrete.cc
│ ├── Concrete.hpp
│ └── Factory.hpp
└── main.cc
我的
CMakeLists.txt
是:
# CMakeLists.txt
cmake_minimum_required(VERSION 3.22)
project(test)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
add_subdirectory(lib)
add_executable(main main.cc)
target_link_libraries(main lib)
# lib/CMakeLists.txt
add_library(lib Abstract.cc Concrete.cc)
target_include_directories(lib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
但是,运行程序时出现以下错误:
terminate called after throwing an instance of 'std::bad_function_call'
事实上,我没有看到表明
Concrete
对象成功注册的日志(请注意登录Factory.hpp
)
花了一些时间搜索这个问题后,我意识到这可能是变量
concrete_registered
未使用的结果,因此链接器没有将其链接到主程序。这就是为什么我需要使用构造未使用的变量的副作用。
我的问题:是否可以强制 C++ 链接器将
concrete_registered
链接到主程序?我知道一些技巧,例如 -Wl,--whole-archive -lXXX -Wl,--no-whole-archive
但我担心链接整个目标可能太多,我只需要这部分代码“未优化”
更好地阅读,这是图书馆运作方式的副作用。当你有一个库 mylib.a 时,它有许多目标文件
文件1.o、文件2.o、文件3.o
当引用它们时,它们被链接器拉入二进制文件。您的系统创建了crete.o,没有导出数据,因此链接器不会将其添加到您的二进制文件中。链接器无法看到从 main.cc 到工厂的抽象查找(在 main.cc 中实现),并且认为为了使系统正常工作,需要将crete.o 添加到二进制文件中。
尝试使用 objdump 查看实现在哪里。
您需要将具体类中的符号拉入二进制文件中,否则它将无法工作