使用时尚未初始化的库中的全局变量
Global variable from a library not yet initialized when used
上下文:
我用 C++ 为 Python 创建了一个模块(我称之为 Hello)。 C++ 和 Python 之间的接口由 Swig 制作。它生成一个动态库 _Hello.so
和一个 Python 文件 Hello.py
。然后,当创建时,我只需要通过这种方式调用它:
python
>>> import Hello
>>> Hello.say_hello()
Hello World !
>>>
我想用许可证保护这个模块,然后我想在导入模块时加载许可证。
我的实现:
为了确保在导入模块时加载许可证,我创建了一个实例化全局变量的单例:
文件LicenseManager.hpp
:
class LicenseManager
{
private:
static LicenseManager licenseManager; // singleton instance
public:
static LicenseManager& get(); // get the singleton instance
public:
LicenseManager(); // load the license
~LicenseManager(); // release the license
private:
LicenseManager(const LicenseManager&); // forbidden (singleton)
LicenseManager& operator = (const LicenseManager&); // forbidden (singleton)
};
文件LicenseManager.cpp
:
LicenseManager LicenseManager::licenseManager; // singleton instance
LicenseManager& LicenseManager::get()
{
return LicenseManager::licenseManager;
}
LicenseManager::LicenseManager()
{
/*
* Here is the code to check the license
*/
if(lic_ok)
std::cout << "[INFO] License found. Enjoy this tool !" << std::endl;
else
{
std::cerr << "[ERROR] License not found..." << std::endl;
throw std::runtime_error("no available licenses");
}
}
LicenseManager::~LicenseManager()
{}
这非常有效!当我加载我的模块时,我得到:
python
>>> import Hello
[INFO] License found. Enjoy this tool !
>>> Hello.say_hello()
Hello World !
>>>
我的问题:
实际上,我的构造函数中检查许可证的代码使用了库 Crypto++。我有一个来自该库的分段错误,但主函数中的相同代码运行良好。然后我认为 Crpyto++ 也使用全局变量,这些在调用 LicenseManager
的构造函数时尚未初始化。
我知道在 C++ 中无法控制全局变量的初始化顺序。但也许还有另一种方法可以更好地满足我的需求?
错误的解决方案:
- 如果单例没有初始化为全局变量,模块导入时不受保护。一种解决方案是在模块的每个功能中添加检查,并强制用户在使用前调用 LicenseManager。这对我来说不是一个解决方案,因为我无法重写我的所有模块来强加它(该模块实际上非常庞大)。
- 在Swig配置文件中,我可以添加Python加载模块时调用的代码。然后我必须编译
Hello.py
以防止用户简单地删除对 LicenseManager 的调用。但它的安全性很差,因为 Python 很容易反编译。
回答我自己的问题。
实际上,可以使用 Swig 添加在库初始化期间调用的 C++ 代码。然后我只需要在 Swig 文件中添加以下代码:
%init%{
LicenseManager::get();
%}
上下文:
我用 C++ 为 Python 创建了一个模块(我称之为 Hello)。 C++ 和 Python 之间的接口由 Swig 制作。它生成一个动态库 _Hello.so
和一个 Python 文件 Hello.py
。然后,当创建时,我只需要通过这种方式调用它:
python
>>> import Hello
>>> Hello.say_hello()
Hello World !
>>>
我想用许可证保护这个模块,然后我想在导入模块时加载许可证。
我的实现:
为了确保在导入模块时加载许可证,我创建了一个实例化全局变量的单例:
文件LicenseManager.hpp
:
class LicenseManager
{
private:
static LicenseManager licenseManager; // singleton instance
public:
static LicenseManager& get(); // get the singleton instance
public:
LicenseManager(); // load the license
~LicenseManager(); // release the license
private:
LicenseManager(const LicenseManager&); // forbidden (singleton)
LicenseManager& operator = (const LicenseManager&); // forbidden (singleton)
};
文件LicenseManager.cpp
:
LicenseManager LicenseManager::licenseManager; // singleton instance
LicenseManager& LicenseManager::get()
{
return LicenseManager::licenseManager;
}
LicenseManager::LicenseManager()
{
/*
* Here is the code to check the license
*/
if(lic_ok)
std::cout << "[INFO] License found. Enjoy this tool !" << std::endl;
else
{
std::cerr << "[ERROR] License not found..." << std::endl;
throw std::runtime_error("no available licenses");
}
}
LicenseManager::~LicenseManager()
{}
这非常有效!当我加载我的模块时,我得到:
python
>>> import Hello
[INFO] License found. Enjoy this tool !
>>> Hello.say_hello()
Hello World !
>>>
我的问题:
实际上,我的构造函数中检查许可证的代码使用了库 Crypto++。我有一个来自该库的分段错误,但主函数中的相同代码运行良好。然后我认为 Crpyto++ 也使用全局变量,这些在调用 LicenseManager
的构造函数时尚未初始化。
我知道在 C++ 中无法控制全局变量的初始化顺序。但也许还有另一种方法可以更好地满足我的需求?
错误的解决方案:
- 如果单例没有初始化为全局变量,模块导入时不受保护。一种解决方案是在模块的每个功能中添加检查,并强制用户在使用前调用 LicenseManager。这对我来说不是一个解决方案,因为我无法重写我的所有模块来强加它(该模块实际上非常庞大)。
- 在Swig配置文件中,我可以添加Python加载模块时调用的代码。然后我必须编译
Hello.py
以防止用户简单地删除对 LicenseManager 的调用。但它的安全性很差,因为 Python 很容易反编译。
回答我自己的问题。
实际上,可以使用 Swig 添加在库初始化期间调用的 C++ 代码。然后我只需要在 Swig 文件中添加以下代码:
%init%{
LicenseManager::get();
%}