Вопрос по dll, visual-studio, c++, singleton – Как реализовать Singleton в приложении с DLL

2

У меня есть приложение (в MS Visual Studio), которое содержит 3 проекта:

main (the one that contains the main function) device (models some hardware device) config (contains some configuration for both other projects)

Итак, граф зависимостей:

main depends on device, which depends on config main depends on config

config Проект содержит Singleton, который содержит некоторые параметры конфигурации.

Я решил повернутьdevice проект в DLL. Когда я сделал это, кажется, что у меня есть два экземпляра Синглтона вconfig проект! Я думаю, это классическая проблема, которая может иметь хорошее решение. Так как я могу это исправить?

Я воспроизвел проблему с помощью следующего (относительно небольшого) кода. Конечно, в моем случае есть около 30 проектов, а не только 3. И я хотел бы сделать только 1 DLL (если это возможно).

<code>// config.h
#pragma once
#include <string>
#include <map>
class Config
{
public:
    static void Initialize();
    static int GetConfig(const std::string& name);

private:
    std::map<std::string, int> data;
};

// config.cpp
#include "config.h"

static Config g_instance;

void Config::Initialize()
{
    g_instance.data["one"] = 1;
    g_instance.data["two"] = 2;
}

int Config::GetConfig(const std::string& name)
{
    return g_instance.data[name];
}
</code>
<code>// device.h
#pragma once

#ifdef _DLL
#define dll_cruft __declspec( dllexport )
#else
#define dll_cruft __declspec( dllimport )
#endif

class dll_cruft Device
{
public:
    void Work();
};

// device.cpp
#include "device.h"
#include <iostream>
#include "config.h"

void Device::Work()
{
    std::cout << "Device is working: two = " << Config::GetConfig("two") << '\n';
}
</code>
<code>// main.cpp
#include <iostream>
#include "config.h"
#include "device.h"

int main()
{
    std::cout << "Before initialization in application: one = " << Config::GetConfig("one") << '\n';
    Config::Initialize();
    std::cout << "After initialization in application: one = " << Config::GetConfig("one") << '\n';
    Device().Work();
    std::cout << "After working in application: two = " << Config::GetConfig("two") << '\n';
}
</code>

Выход:

Before initialization in application: one = 0

After initialization in application: one = 1

Device is working: two = 0

After working in application: two = 2

Некоторые пояснения о том, что делает код и почему:

Main application starts The first print is just to show that the singleton is not initialized yet Main application initializes the singleton The first print shows that the initialization worked Main application starts the "hardware device" Inside the DLL, the singleton is not initialized! I expect it to output two = 2 The last print shows that the singleton is still initialized in main application
Вы поменяли строки 5 и 4 на главном или я ошибаюсь? kovarex
@ Марвин Да, это исправить ... anatolyg

Ваш Ответ

3   ответа
-2

Я считаю, что определение и доступ к экземпляру синглтона таким образом может решить вашу проблему:

Config& getInstance()
{
  static Config config;
  return config;
}

Таким образом, вам также не нужно иметь (и вызывать) метод Initialize, вы можете использовать конструктор для инициализации, который будет вызываться автоматически при первом вызове getInstance.

код, содержащий статическую переменную, должен находиться только в одном месте. Кто его использует, не имеет значения.
Называй меня глупым, но я все еще не понимаю. Теперь у вас есть специальный dll для singleton, он по-прежнему используется как main, так и config, и в результате вы получаете две статические переменные.
Проблема в том, что код получает два разных экземпляра статическогоconfig переменная.
Хорошо, я, наверное, не прав, я просто хочу это понять. В чем разница между этим и вашим ответом, это практически DLL, единственной целью которой является управление экземпляром одной тонны сейчас
В оригинальном вопросе обаmain приложение иdevice DLL связатьconfig библиотека, в результате чего две копии функции и две статические переменные. Мое решение состоит в том, чтобы переместить эту функцию в свою собственную DLL, чтобы она больше не была частью связываемой библиотеки.
2

Когда я столкнулся с этой же проблемой, я решил ее, создав еще одну DLL, единственной целью которой является управление экземпляром singleton. Все попытки получить указатель на синглтон вызывают функцию внутри этой новой DLL.

@JeffTrull, если символы доступны в статической библиотеке, тогда не имеет значения, были ли они экспортированы из DLL или нет; статическая библиотека имеет приоритет. И вы все равно получите несколько копий.
По моему собственному (ограниченному) опыту это единственный путь. Я думаю, что если бы я действительно ненавидел идею создания другой DLL, я мог бы попытаться экспортироватьconfig символы изdevice DLL и посмотреть, будет ли загрузчик Windows сопоставить их с теми же объектами вmain, Но, насколько я знаю, ваше единственное разумное решение.
2

Вы можете решить, где должен находиться синглтон, а затем показать его другим потребителям.

Отредактировано OP:

Например, я хочу, чтобыconfig Экземпляр появляется только в EXE (не DLL).

Turn the instance into a pointer

static Config* g_instance;

Add a separate initializing function to device's exported functions:

void InitializeWithExisting(Config* instance) {g_instance=instance;}

After initializing the singleton normally, use the second initialization:

Config::Initialize();
Config::InitializeWithExisting();
Config :: InitializeWithExisting (); аргумент отсутствует. Этот код не может скомпилироваться.
В DLL есть специальная функция, с помощью которой вы регистрируете свой экземпляр,SetConfig(Config* pConfig), После этого DLL может использоватьConfig
Как мне это сделать? Я предполагаю, что по "где" Вы имеете в виду «exe или dll»; предположим, я решил "exe", так что мне делать дальше? anatolyg
Кажется, отличная идея! anatolyg

Похожие вопросы