Регистрация обработчиков файлов в коде, выполняемом до main () - PullRequest
0 голосов
/ 01 марта 2020

Я ищу способы предотвратить ненужный беспорядок в коде установки в main(), а также в других местах. У меня часто есть тонны установочного кода, который регистрируется на какой-то фабрике. Стандартный пример, например, обработчики для различных типов файлов.

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

test. cc:

int main() {
  return 0;
}

loader.h:

#ifndef LOADER_H_
#define LOADER_H_

#include <functional>

namespace loader {

class Loader {
 public:
  Loader(std::function<void()> f);
};

}  // namespace loader

#define REGISTER_HANDLER(name, f) \
namespace { \
::loader::Loader _macro_internal_ ## name(f); \
}

#endif  // LOADER_H_

loader. cc:

#include "loader.h"

#include <iostream>

namespace loader {

Loader::Loader(std::function<void()> f) { f(); }

}  // namespace loader

a . cc:

#include <iostream>

#include "loader.h"

REGISTER_HANDLER(a, []() {
  std::cout << "hello from a" << std::endl;
})

Идея заключается в том, что a.cc будет в реальном приложении, например, вызывать некоторый метод, где он сам регистрирует себя в качестве обработчика для определенного типа файла. Компиляция кода с помощью c++ -std=c++11 test.cc loader.cc a.cc создает двоичный файл, который печатает «привет от», в то время как c++ -std=c++11 test.cc loader.cc хранит молчание.

Мне интересно, есть ли что-то тонкое, с чем мне, возможно, следует быть осторожным? Например, если кто-то создает сложные объекты в лямбде, который здесь запускается, я предполагаю, что во время очистки могут произойти странные вещи, например, в многопоточном приложении?

Ответы [ 2 ]

2 голосов
/ 01 марта 2020

Вы писали:

... ненужный беспорядок в коде установки в main() ...

int main() {
    return 0;
}

Это не предотвращает ненужный беспорядок. Это скрывает ваши инициализации. Они все еще происходят, но теперь вы должны преследовать их. Это действительно не способ сделать это. Кроме того, это заставит использовать большое количество глобальных состояний - скорее всего, во многих независимых глобальных переменных - что тоже плохо. Вместо этого, попробуйте написать что-то вроде:

class my_app_state { /* ... */ };

my_app_state initialize(/* perhaps with argc and argv here? */) {
    // 
    // Your "unnecessary" clutter goes here...
    //
    return whatever;
}

int main() {
    auto app_state = initialize();
    //
    // do stuff involving the app_state...
    //
}

и не пытайтесь «запустить» загрузчик программы.

1 голос
/ 01 марта 2020

Этот подход не гарантированно сработает:

[basi c .start.dynamic] / 4 Это определяется реализацией, если инициализация Dynami c нелокальной не встроенной переменной со значением c длительность хранения упорядочивается перед первым оператором main или откладывается. Если оно откладывается, то это происходит до того, как любое неинициализирующее использование odr любой не встроенной функции или не встроенной переменной, определенной в той же единице перевода, что и инициализируемая переменная.

Таким образом инициализация _macro_internal_a может быть отложена до тех пор, пока не будет использовано что-либо в a.cc. И поскольку в a.cc фактически ничего не используется, инициализация может вообще не выполняться.

На практике компоновщики имеют тенденцию отбрасывать объектные файлы, на которые, по-видимому, не ссылается что-либо в программе (особенно когда эти файлы приходят из библиотек).

...