Подтвердить что ты не робот

Запуск кода на С++ вне сферы действия

(я знаю). В С++ я может declare variable выйти из области видимости, а я не может запускать любой код/​​оператор, кроме для инициализации глобальных/статических переменных.


IDEA

Можно ли использовать ниже сложный код для того, чтобы (например) выполнить некоторые манипуляции std::map?

Здесь я использую void *fakeVar и инициализирую его через Fake::initializer() и делаю все, что хочу в нем!

std::map<std::string, int> myMap;

class Fake
{
public:
    static void* initializer()
    {
        myMap["test"]=222;
        // Do whatever with your global Variables

        return NULL;
    }
};

// myMap["Error"] = 111;                  => Error
// Fake::initializer();                   => Error
void *fakeVar = Fake::initializer();    //=> OK

void main()
{
    std::cout<<"Map size: " << myMap.size() << std::endl; // Show myMap has initialized correctly :)
}
4b9b3361

Ответ 1

Один из способов его решения - иметь класс с конструктором, который делает вещи, а затем объявлять фиктивную переменную этого класса. Как

struct Initializer
{
    Initializer()
    {
        // Do pre-main initialization here
    }
};

Initializer initializer;

Конечно, у вас может быть несколько таких классов, выполняющих разную инициализацию. Порядок в каждой единицы перевода указан сверху вниз, но порядок между единицами перевода не указан.

Ответ 2

Вам не нужен поддельный класс... вы можете инициализировать, используя lambda

auto myMap = []{
    std::map<int, string> m;
    m["test"] = 222;
    return m;
}();

Или, если это просто данные, инициализируйте карту:

std::map<std::string, int> myMap { { "test", 222 } };

Ответ 3

Хорошо ли использовать ниже хитрый код, чтобы (например) выполните некоторую std:: map манипуляцию?

Нет.

Любое решение, связанное с изменяемыми нелокальными переменными, является ужасной идеей.

Ответ 4

Это хорошая идея...?

Не совсем. Что, если кто-то решит, что в своей "сложной инициализации" они хотят использовать вашу карту, но в какой-то системе или по непонятной причине после определенного перехвата ваша карта заканчивается инициализацией после их попытки использования? Если вместо этого вы вызываете статическую функцию, которая возвращает ссылку на карту, тогда она может инициализировать ее при первом вызове. Сделайте карту статической локальной переменной внутри этой функции и вы прекратите любое случайное использование без этой защиты.

Ответ 5

§ 8.5.2 состояния

За исключением объектов, объявленных с помощью спецификатора constexpr, для которых см. 7.1.5, инициализатор в определении переменной может состоять из произвольных выражений, содержащих литералы и ранее объявленных переменные и функции, независимо от продолжительности хранения переменных

поэтому то, что вы делаете, вполне допускается стандартом С++. Тем не менее, если вам нужно выполнить "операции инициализации", лучше просто использовать конструктор класса (например, обертку).

Ответ 6

То, что вы сделали, является совершенно законным С++. Таким образом, если это работает для вас и поддерживается и понятно любому, кто работает с кодом, все в порядке. Однако образец Йоахима Пилеборга ясен.

Одна проблема с инициализацией глобальных переменных, таких как это может произойти, если они используют друг друга во время инициализации. В этом случае может оказаться сложной задачей обеспечить инициализацию переменных в правильном порядке. По этой причине я предпочитаю создавать функции InitializeX, InitializeY и т.д. И явно вызывать их в правильном порядке из главной функции.

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

Итак, пойдите, если это сработает для вас, но будьте в курсе подводных камней. Тот же самый совет относится ко всем функциям С++!

В своем вопросе вы сказали, что сами думаете, что код "сложный". Для этого нет необходимости слишком усложнять ситуацию. Итак, если у вас есть альтернатива, которая кажется вам менее "сложной"... это может быть лучше.

Ответ 7

Когда я слышу "хитрый код", я сразу думаю о запахах кода и кошмарах обслуживания. Чтобы ответить на ваш вопрос, нет, это не очень хорошая идея. Хотя это действительно код С++, это плохая практика. Существуют и другие, гораздо более явные и значимые альтернативы этой проблеме. Чтобы уточнить, что ваш метод initializer() возвращает void * NULL бессмысленна, поскольку намерение вашей программы идет (т.е. каждая строка вашего кода должна иметь значимую цель), и теперь у вас есть еще одна ненужная глобальная переменная fakeVar, что бесполезно указывает на NULL.

Рассмотрим несколько менее "сложных" альтернатив:

  • Если чрезвычайно важно, чтобы у вас был только один глобальный экземпляр myMap, возможно, использование Singleton Pattern было бы более подходящим, и вы могли бы лениво инициализировать содержимое myMap когда они необходимы. Имейте в виду, что шаблон Singleton имеет свои проблемы.

  • Попросите статический метод создать и вернуть карту или использовать глобальное пространство имен. Например, что-то вроде этого:

    // global.h
    namespace Global
    {
        extern std::map<std::string, int> myMap;
    };
    
    // global.cpp
    namespace Global
    {
        std::map<std::string, int> initMap()
        {
            std::map<std::string, int> map;
            map["test"] = 222;
            return map;
        }
    
        std::map<std::string, int> myMap = initMap();
    };
    
    // main.cpp
    #include "global.h"
    
    int main()
    {
       std::cout << Global::myMap.size() << std::endl;
       return 0;
    }
    
  • Если это карта со специализированной функциональностью, создайте свой собственный класс (лучший вариант)! Хотя это не полный пример, вы получаете идею:

    class MyMap
    {
    private:
        std::map<std::string, int> map;
    
    public:
    
        MyMap()
        {
            map["test"] = 222;
        }
    
        void put(std::string key, int value)
        {
            map[key] = value;
        }
    
        unsigned int size() const
        {
            return map.size();
        }
    
        // Overload operator[] and create any other methods you need
        // ...
    };
    
    MyMap myMap;
    
    int main()
    {
       std::cout << myMap.size() << std::endl;
       return 0;
    }
    

Ответ 8

В С++ у вас не может быть операторов вне любой функции. Тем не менее, у вас есть объявленные глобальные объекты, а вызов конструктора (инициализатор) для этих глобальных объектов автоматически перед основными запусками. В вашем примере fakeVar является глобальным указателем, который инициализируется функцией класса static scope, это абсолютно нормально.
Даже глобальный объект мог бы обеспечить, чтобы глобальный конструктор объектов выполнял желаемый инициализатор. Например,

class Fake
{
public:
    Fake()     {
        myMap["test"]=222;
        // Do whatever with your global Variables
    }
};
Fake fake; 

Ответ 9

Это случай, когда единичные сборки (сборка единичной единицы перевода) могут быть очень мощными. Макрос __COUNTER__ является стандартом де-факто среди компиляторов C и С++, и с его помощью вы можете написать произвольный императивный код в глобальной области:

// At the beginning of the file...
template <uint64_t N> void global_function() { global_function<N - 1>(); } // This default-case skips "gaps" in the specializations, in case __COUNTER__ is used for some other purpose.
template <> void global_function<__COUNTER__>() {} // This is the base case.

void run_global_functions();

#define global_n(N, ...) \
template <> void global_function<N>() { \
    global_function<N - 1>(); /* Recurse and call the previous specialization */ \
    __VA_ARGS__; /* Run the user code. */ \
}
#define global(...) global_n(__COUNTER__, __VA_ARGS__)

// ...

std::map<std::string, int> myMap;

global({
    myMap["test"]=222;
    // Do whatever with your global variables
})
global(myMap["Error"] = 111);

int main() {
    run_global_functions();
    std::cout << "Map size: " << myMap.size() << std::endl; // Show myMap has initialized correctly :)
}

global(std::cout << "This will be the last global code run before main!");


// ...At the end of the file

void run_global_functions() {
    global_function<__COUNTER__ - 1>();
}

Это особенно эффективно, если вы осознаете, что можете использовать его для инициализации статических переменных без зависимости от времени выполнения C. Это означает, что вы можете создавать очень маленькие исполняемые файлы, не избегая ненулевых глобальных переменных:

// At the beginning of the file...
extern bool has_static_init;
#define default_construct(x) x{}; global(if (!has_static_init()) new (&x) decltype(x){})
// Or if you don't want placement new:
// #define default_construct(x) x{}; global(if (!has_static_init()) x = decltype(x){})

class Complicated {
    int x = 42;
    Complicated() { std::cout << "Constructor!"; }
}
Complicated default_construct(my_complicated_instance); // Will be zero-initialized if the CRT is not linked into the program.

int main() {
    run_global_functions();
}

// ...At the end of the file
static bool get_static_init() {
    volatile bool result = true; // This function can't be inlined, so the CRT *must* run it.
    return result;
}
has_static_init = get_static_init(); // Will stay zero without CRT

Ответ 10

Этот ответ похож на ответ программиста, но его можно считать чище. Начиная с С++ 17 (то есть, когда был добавлен std::invoke()), вы можете сделать что-то вроде этого:

#include <functional>

auto initializer = std::invoke([]() {
    // Do initialization here...

    // The following return statement is arbitrary. Without something like it,
    // the auto will resolve to void, which will not compile:
    return true;
});