Передача значений в atexit - PullRequest
       29

Передача значений в atexit

3 голосов
/ 08 ноября 2011

Я хочу выдвинуть серию функций очистки по мере необходимости.Я использовал atexit, чтобы сделать это для одной функции очистки без каких-либо параметров, но я не уверен, как расширить этот подход для более чем одной функции очистки.Я не очень знаком с boost :: bind, но предположил, что это было бы хорошей идеей, поскольку именно так я связывал свои функции с потоками ...

В C ++ я пытаюсь заставить работать следующее:

Определение функции

static void closeAnimation(string prefix="");// static member of fileWriter

Код:

atexit(boost::bind(fileWriter::closeAnimation, "0")); // I want to first prefix to be "0"

Ошибка:

cannot convert ‘boost::_bi::bind_t<void, void (*)(std::basic_string<char>), boost::_bi::list1<boost::_bi::value<const char*> > >’ to ‘void (*)()’ for argument

Заранее спасибо!

Ответы [ 3 ]

3 голосов
/ 08 ноября 2011

atexit является устаревшей функцией C и не очень адаптирована к C ++. Вы можете зарегистрировать более одной функции с помощью atexit, но все они должны быть void (*)(), без boost::function и без аргументов.

В C ++ большинство, если не все функциональные возможности atexit включены деструкторами статических объектов. В вашем случае я бы написал что-нибудь как:

#include <vector>

class CallInDestructor
{
    class Registry
    {
        std::vector<CallInDestructor*> myInstances;
    public:
        register_caller(CallInDestructor* caller)
        {
            myInstances.push_back(caller);
        }
        ~Registry()
        {
            while ( !myInstances.empty() ) {
                delete myInstances.back();
                myInstances.pop_back();
            }
        }
    };
    static Registry& registry()
    {
        static Registry theOneAndOnly;
        return theOneAndOnly;
    }

protected:
    CallInDestructor() { registry().register_caller( this ); }

public:
    virtual ~CallInDestructor() {}
};

template<typename Fnc> void callAtExit( Fnc fnc );

template<typename Fnc>
class ConcreteCallInDestructor : public CallInDestructor
{
    Fnc myFnc;
    ConcreteCallInDestructor( Fnc fnc = Fnc() ) : myFnc( fnc ) {}
    virtual ~ConcreteCallInDestructor() { myFnc(); }

    friend void callAtExit<Fnc>( Fnc fnc );
};

template<typename Fnc>
void
callAtExit( Fnc fnc )
{
    new ConcreteCallInDestructor<Fnc>( fnc );
}

Используйте callAtExit как и atexit, но оно должно работать для всего который может быть вызван без аргументов (включая boost::function). Или вы можете написать свои собственные классы, производные от CallInDestructor, как Пока вы предпринимаете шаги, чтобы гарантировать, что все экземпляры динамически распределенный (так как конструктор регистрирует объект так, чтобы он был удален); эти классы могут содержать любые дополнительные данные, которые вы хотите.

2 голосов
/ 08 ноября 2011

Не существует «однострочного решения без усложнения вашего кода».

Худшее решение - сохранить этот параметр в глобальной переменной и извлечь его из обработчика atexit

Поскольку вы используете C ++, деструктор статической переменной также может служить обработчиком atexit. Затем вы можете передать параметр в конструктор этой статической переменной для параметризации, например,

struct AtExitAnimationCloser
{
    const char* _which_param;

    AtExitAnimationCloser(const char* which_param) : _which_param(which_param) {}
    ~AtExitAnimationCloser() { FileWriter::closeAnimation(_which_param); }
};

void f()
{
    printf("entering f\n");

    static AtExitAnimationCloser s0 ("0"); // registers closeAnimation("0") at exit
    static AtExitAnimationCloser s1 ("1"); // registers closeAnimation("0") at exit

    printf("leaving f\n");
}

Демонстрация: http://www.ideone.com/bfYnY

Обратите внимание, что статические переменные связаны с их именем, поэтому вы не можете сказать

for (it = vecs.begin(); it != vecs.end(); ++ it)
{
   static AtExitAnimationCloser s (*it);
}

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

static AnotherAtExitAnimationCloser s (vecs.begin(), vecs.end())

Наконец, с идиоматическим C ++ я не думаю, что вам нужно использовать эти приемы ... Вы могли бы хранить вектор типов T, который при уничтожении ~ T вызывает fileWriter::closeAnimation.

0 голосов
/ 08 ноября 2011

Проблема в том, что bind возвращает объект функции, а atexit получает указатель на функцию, которая возвращает void и не принимает параметров.

Вы можете попробовать это:

void fun() {
    fileWriter::closeAnimation("0");
}

atexit(fun);
...