Любой шаблон RAII в boost или C++0x

Есть ли какой-либо шаблон, доступный в boost для RAII. Есть такие классы, как scoped_ptr, shared_ptr, которые в основном работают с указателем. Можно ли использовать эти классы для любых других ресурсов, кроме указателей. Есть ли шаблон, который работает с общими ресурсами.

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

Edit: Как насчет C++0x с поддержкой лямбда-функций?


person Yogesh Arora    schedule 23.01.2010    source источник
comment
Это лямбда, а не лямбада :) Исправил для вас. ;)   -  person jalf    schedule 23.01.2010
comment
Не следует ли теперь называть его C++1x?   -  person LiraNuna    schedule 23.01.2010
comment
@LiraNuna: Не совсем, потому что это имя-заполнитель. Его единственная цель — быть удобным и гарантировать, что все знают, что имеется в виду. C++1x неоднозначен, потому что 1) мы не привыкли к этому имени и 2) в течение этого десятилетия может появиться более одной версии стандарта. C++0x — это тот, к которому все привыкли, и ничего другого он не может означать.   -  person jalf    schedule 23.01.2010


Ответы (6)


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

person sth    schedule 23.01.2010
comment
Пользовательские средства удаления — это прекрасно, но что, если тип, которым вы хотите управлять, не является указателем? Это часто происходит при обертывании библиотек C, которые используются как example_t data; example_new(&data); /* manipuate the data ... */ example_free(&data), и если эти объекты хранятся в каком-то постоянном контейнере, вам также необходимо хранить управляемый указатель, что было бы непрактично и расточительно. - person Ponkadoodle; 03.09.2015

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

Но, честно говоря, я не понимаю, зачем тебе это. Хотя я понимаю, что немного раздражает каждый раз писать класс для шаблона одноэтапного получения и одноэтапного выпуска, вы говорите о многоэтапном приобретении и выпуске.
Если было выполнено несколько шагов , он, на мой взгляд, принадлежит к служебному классу с соответствующим названием, так что детали скрыты, а код на месте (таким образом снижается вероятность ошибки). есть о чем беспокоиться.

person Georg Fritzsche    schedule 23.01.2010
comment
жизнь была бы намного проще, если бы Страутроп, наконец, разрешил перейти на С++ (он категорически отказывается разрешить это, он считает, что у вас должен быть класс каждый раз) - person pm100; 23.01.2010
comment
Честно говоря, я редко чувствую необходимость в том, что упоминает ОП, потому что каждый повторяющийся шаблон использования ресурсов должен относиться к служебному классу. Дополнительный бонус - вам не нужно запоминать детали обработки ресурсов. - person Georg Fritzsche; 23.01.2010
comment
@pm100: Нет, у тебя это вверх дном. Наконец, это означало бы, что мне придется писать код очистки каждый раз, когда я использую класс. RAII, основанный на деструкторе, означает, что я могу написать код очистки один раз для каждого класса. Как, наконец, снова хорошая идея? - person jalf; 23.01.2010
comment
@jalf: Аминь! Наконец-то мерзость! - person Drew Hall; 23.01.2010
comment
Наконец, это хорошая идея, когда вы хотите написать код очистки, который возникает только в одном месте и несовместим между разными пользователями одного и того же ресурса. то есть никогда. Нет, я имею в виду: например, когда вы хотите добавить к чему-то быструю трассировку или написать транзакцию (которая почти всегда уникальна и используется только в одном месте). Очевидно, что вы можете написать класс для представления вашей транзакции, но он может стать немного корпоративным, если вам придется сделать это, следовательно, различные средства защиты области действия, которые хороши (даже лучше), если только вы не снова привык, наконец, и неудобно изучать стили кодирования. - person Steve Jessop; 23.01.2010
comment
если я приобрету что-то разовое - то хочу использовать, наконец, чтобы выпустить. В противном случае я должен написать одноразовый класс. Я знаю, что для общих классов ресурсов я должен писать классы-оболочки (а в моем коде их полно). Я возражаю против того, чтобы меня заставляли делать в каждом случае - person pm100; 26.01.2010

Более общая и более эффективная (без вызова через указатель функции) версия выглядит следующим образом:

#include <boost/type_traits.hpp>

template<typename FuncType, FuncType * Func>
class RAIIFunc
{
public:
   typedef typename boost::function_traits<FuncType>::arg1_type arg_type;
   RAIIFunc(arg_type p) : p_(p) {}
   ~RAIIFunc() { Func(p_); }
   arg_type & getValue() { return p_; }
   arg_type const & getValue() const { return p_; }
private:
   arg_type p_;
};

Пример использования:

RAIIFunc<int (int), ::close> f = ::open("...");
person Buğra Gedik    schedule 12.03.2011
comment
Я поставил это решение через реальную компиляцию, и оно мне понравилось. Мне пришлось внести некоторые незначительные изменения, но ничего страшного. - person Chris Cleeland; 16.02.2012

Должен признаться, я действительно не вижу смысла. Написание оболочки RAII с нуля уже смехотворно просто. Просто не так много работы, которую можно сэкономить, используя какую-то предопределенную оболочку:

struct scoped_foo : private boost::noncopyable {
  scoped_foo() : f(...) {}
  ~scoped_foo() {...}

  foo& get_foo() { return f; }

private:
  foo f;
};

Теперь ... — это, по сути, биты, которые нужно было бы заполнить вручную, если бы вы использовали какой-то общий шаблон RAII: создание и уничтожение нашего ресурса foo. А без них действительно мало что остается. Несколько строк шаблонного кода, но их так мало, что кажется нецелесообразным извлекать их в повторно используемый шаблон, по крайней мере, на данный момент. С добавлением лямбда-выражений в C++0x мы могли написать функторы для создания и уничтожения настолько лаконично, что может быть стоит написать их и подключить к повторно используемому шаблону. Но до тех пор кажется, что это будет больше проблем, чем пользы. Если бы вам нужно было определить два функтора для включения в шаблон RAII, вы бы уже написали большую часть этого стандартного кода дважды.

person jalf    schedule 23.01.2010
comment
лямбды, да, это то, о чем я думал. извините, что не упомянул об этом в моем вопросе (хотя это было в моем уме) - person Yogesh Arora; 23.01.2010
comment
Насколько я знаю, C++0x не определяет такой шаблон. Возможно, потому, что это так просто сделать, нет особого смысла. Возможно, они ждут, пока Boost или другие третьи стороны определят его и дадут ему проявить себя в реальном мире, прежде чем стандартизировать его. На данный момент мы еще не знаем, 1) стоит ли это усилий и 2) есть ли какие-либо неочевидные ошибки, с которыми должен справиться шаблон. - person jalf; 23.01.2010

Я думал о чем-то подобном:

template <typename T>
class RAII {
    private:
        T (*constructor)();
        void (*destructor)(T);
    public:
        T value;
        RAII(T (*constructor)(), void (*destructor)(T)) : 
                    constructor(constructor), 
                    destructor(destructor) {
            value = constructor();
        }
        ~RAII() {
            destructor(value);
        }
};

и для использования следующим образом (на примере OpenGL GLUquadric):

RAII<GLUquadric*> quad = RAII<GLUquadric*>(gluNewQuadric, gluDeleteQuadric);
gluSphere(quad.value, 3, 20, 20)
person Lie Ryan    schedule 10.09.2010

Вот еще один помощник C++11 RAII: https://github.com/ArtemGr/libglim/blob/master/raii.hpp

Он запускает функтор C++ при уничтожении:

auto unmap = raiiFun ([&]() {munmap (fd, size);});
person ArtemGr    schedule 29.06.2013
comment
Очень опасная реализация: требуется, чтобы происходило копирование/перемещение. Если этого не происходит, деструктор объекта RAIIFun запускается более одного раза! - person j6t; 27.10.2016
comment
@ j6t Он использует Perfect Forwarding и, безусловно, не требует исключения для работы. Если вы видите какой-то крайний случай, когда он запускает функтор дважды, предоставьте MCVE. - person ArtemGr; 28.10.2016
comment
Трудно предоставить MCVE, так как удаление копии — это не гарантия, а опция для компилятора. Проблема возникает, когда функция raiiFun возвращает объект типа RAIIFun. Если копирование/перемещение не исключено, деструктор вызывается до завершения return. Существует еще одно копирование/перемещение, которое может быть исключено или не исключено при инициализации локальной переменной из результата функции: если временная переменная не исключена, вызывается ее деструктор. В C++17 мы получаем гарантированное удаление копии, и проблема исчезает. - person j6t; 28.10.2016
comment
@ j6t IIRC, в return RAIIFun<Fun> (fun) нет копии, потому что RAIIFun<Fun> (fun) уже является значением r. Copy elision обрабатывает другой вариант использования, например auto f = RAIIFun<Fun> (fun); return f;. Когда вы возвращаете rvalue, нет необходимости в удалении копии. Также нет копии в operator =, потому что мы используем конструкторы = default;. - person ArtemGr; 28.10.2016
comment
Хотя это факт, что с -std=c++11 -O0 -fno-elide-constructors gcc трижды вызывает функтор, так что этим действительно можно выстрелить себе в ногу. - person ArtemGr; 28.10.2016
comment
Правильно. Вы во власти компилятора. - person j6t; 28.10.2016