代码之家  ›  专栏  ›  技术社区  ›  Hans

避免在C++中优化未使用的变量?

  •  0
  • Hans  · 技术社区  · 2 年前

    我试图使用构建一个未使用的变量的副作用,所以我不希望它被优化掉。

    好的,我知道在构造变量时使用副作用可能不是一个好的做法。请允许我解释一下目前的情况。

    我有一节抽象课和一节具体课。我已经建立了一个生产 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'
    

    事实上,我没有看到显示成功注册的日志 混凝土 对象(注意登录 Factory.hpp )

    在花了一些时间搜索这个问题后,我意识到这可能是变量的结果 混凝土注册 未使用,因此链接器不会将其链接到主程序。这就是为什么我需要使用为未使用的变量构造的副作用。

    我的问题是:是否可以强制C++链接器链接 混凝土注册 到主程序?我知道一些技巧,比如 -Wl,--whole-archive -lXXX -Wl,--no-whole-archive 但我担心链接整个目标可能太多了,我只需要这部分代码“没有优化”

    0 回复  |  直到 2 年前
        1
  •  1
  •   mksteve    2 年前

    更好地阅读,这是图书馆工作方式的副作用。当你有一个库mylib.a时,它有很多对象文件

    file1.o、file2.o、file3.o

    当它们被引用时,链接器会将它们拉入二进制文件中。您的系统创建了具体的.o,没有导出数据,因此链接器不会将其添加到二进制文件中。链接器看不到从main.cc到工厂的抽象查找(在main.cc中实现),并认为系统要工作,需要将具体的.o添加到二进制文件中。

    尝试使用objdump查看实现的位置。

    您需要将具体类中的一个符号拉入二进制文件,否则它将不起作用