LINUX.ORG.RU

[c++] CL's with-open-file like macro in C++

 


0

0

Хотелось бы сделать подобное на С++. Внизу приведен текст proba.cpp, методы инициализации и деинициализации без параметров. Как бы сделать так чтобы можно было предавать методы инициализации и деинициализации с одним параметром, который передается тоже через шаблон?

/// proba.cpp

#include <iostream>

template<typename C>
class WithActiveObject
{
public:
    typedef bool (C::*InitCallback0)();
    typedef void (C::*ShutdownCallback0)();

    WithActiveObject(C& object, InitCallback0 init, ShutdownCallback0 shutdown)
        : _status(false),
          _object(&object),
          _init0(init),
          _shutdown0(shutdown)
    {
        _status = (_object->*_init0)();
    }

    ~WithActiveObject()
    {
        if(_status)
        {
            (_object->*_shutdown0)();
        }
    }

    bool status() const
    {
        return _status;
    }

private:
    bool _status;

    C *_object;

    InitCallback0 _init0;
    ShutdownCallback0 _shutdown0;

private:
    WithActiveObject();
    WithActiveObject(const WithActiveObject&);
    WithActiveObject& operator=(const WithActiveObject&);
};

class MyDevice
{
public:
    MyDevice()
    {
        std::cout << "MyDevice created." << std::endl;
    }

    ~MyDevice()
    {
        std::cout << "MyDevice destroyed." << std::endl;
    }

    bool init()
    {
        std::cout << "MyDevice initialized" << std::endl;

        return true;
    }

    void shutdown()
    {
        std::cout << "MyDevice uninitialized" << std::endl;
    }

    bool doSomething()
    {
        return false;
    }
};

void test_device(MyDevice& device)
{
    WithActiveObject<MyDevice> obj(device, &MyDevice::init, &MyDevice::shutdown);

    std::cout << "Testing device..." << std::endl;
    if(!device.doSomething())
    {
        std::cerr << "Failed to test device." << std::endl;

        return;
    }
}

int main()
{
    MyDevice device;

    test_device(device);

    return 0;
}

output: if doSomething() returns false:

MyDevice created.
MyDevice initialized
Testing device...
Failed to test device.
MyDevice uninitialized
MyDevice destroyed.

output: if doSomething() returns true:

MyDevice created.
MyDevice initialized
Testing device...
MyDevice uninitialized
MyDevice destroyed.
anonymous

Хотелось бы сделать подобное на С++. Внизу приведен текст proba.cpp, методы инициализации и деинициализации без параметров. Как бы сделать так чтобы можно было предавать методы инициализации и деинициализации с одним параметром, который передается тоже через шаблон

anonymous
()
Ответ на: комментарий от den73

Лисп не может быть использован по ряду причин не зависящих от меня. Нужно имеено вот эту фичу.

anonymous
()
Ответ на: комментарий от anonymous

Неужели никакой препроцессор нельзя использовать? Даже m4?

den73 ★★★★★
()

А пошли в асю обсудим всё по-быстрому в режиме "Э?", потом сюда лог выложим с решением. Я думаю, оно найдёццо (-;

(кто в море утонет, купаться больше не будет!)

icq 297566544

kira89
()

Может я чего-то недопонял. Но что мешает сделать так.

template<typename C,typename B>
class WithActiveObject
{
public:
    typedef bool (C::*InitCallback0)(const B &);
    typedef void (C::*ShutdownCallback0)(const B &);
...

или так

template<typename C>
class WithActiveObject
{
public:
    typedef bool (C::*InitCallback0)(const typename С::init_type &);
    typedef void (C::*ShutdownCallback0)(const typename C::shutdown_type &);
...
class MyDevice
{
   public:
       typedef int init_type;
       typedef bool shutdown_type;

       void init(const init_type &);
       void shutdown(const shutdown_type &);
...



Vinick ★★
()
Ответ на: комментарий от kira89

Заседание комиссии по булочничеству пришло к заключению.

Афтар хочет дать возможность пользователю шаблона управлять тем, какой объект будет передан в качестве аргумента методу инициализации и деинициализации.

Так как через шаблон можно передавать либо имена типов, либо именя других шаблонов, либо int-ы, комиссия заключила, что целесообразно применение классов-стратегий.

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

kira89
()
Ответ на: комментарий от Vinick

> Может я чего-то недопонял. Но что мешает сделать так.

я ТО ЖЕ САМОЕ недопонял.

Автор, напиши как ты собираешься вызывать то что тебе хочется (usage case). И заодно очень любопытно, где такая вещь трубуется, если правда того, что написал Vinick, недостаточно!

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от www_linux_org_ru

Хотелось бы именно вот этого. Понятно, что приведенный код работать не будет.

class Data;

class Session
{
public:
bool init(Data&);

void shutdown(Data&);
};


void process_session(Session& session, Data& data)
{
WithActiveObject<Session> obj(session, &Session::init(data), &Session::shutdown(data));
// WithActiveObject() constructor calls Session::init(data)

if(error)
{
// got error...

return;
// ~WithActiveObject() destructor calls Session::shutdown(data)
}

// ~WithActiveObject() destructor calls Session::shutdown(data)
}

int main()
{
Session session;
Data data;

process_session(session, data)

return 0;
}

anonymous
()

Solved.

template<typename C, typename B>
class WithActiveObject1
{
public:
    typedef bool (C::*InitCallback1)(B&);
    typedef void (C::*ShutdownCallback1)(B&);

    WithActiveObject1(C& object, B& data, InitCallback1 init, ShutdownCallback1 shutdown)
        : _status(false),
          _object(&object),
          _data(data),
          _init1(init),
          _shutdown1(shutdown)
    {
        _status = (_object->*_init1)(_data);
    }

    ~WithActiveObject1()
    {
        if(_status)
        {
            (_object->*_shutdown1)(_data);
        }
    }

    bool status() const
    {
        return _status;
    }

private:
    bool _status;

    C *_object;
    B& _data;

    InitCallback1 _init1;
    ShutdownCallback1 _shutdown1;

private:
    WithActiveObject1();
    WithActiveObject1(const WithActiveObject1&);
    WithActiveObject1& operator=(const WithActiveObject1&);
};

class MyDevice1
{
public:
    MyDevice1()
    {
        std::cout << "MyDevice1 created." << std::endl;
    }

    ~MyDevice1()
    {
        std::cout << "MyDevice1 destroyed." << std::endl;
    }

    bool init(int& a)
    {
        std::cout << "MyDevice1 initialized: " << a << std::endl;

        return true;
    }

    void shutdown(int& a)
    {
        std::cout << "MyDevice1 uninitialized: " << a << std::endl;
    }

    bool doSomething()
    {
        return false;
    }
};

    WithActiveObject1<MyDevice1, int> obj(device, res, &MyDevice1::init, &MyDevice1::shutdown);

anonymous
()
Ответ на: комментарий от anonymous

Ох емае, неужто нельзя покороче как, через шаблоны?
На Лиспе это элементарно делается:

(defmacro with-open-fl ((var filename &rest open-args) &body body)
  (let ((,var (open ,filename ,@open-args)))
    (unwind-protect 
         (progn ,@body)
         (close ,var))))

progn эквивалентно { ... }, последнее значение возвращается как значение всей формы
let - специальная форма, первый список в ней лексически биндит аргументы, остальное - выполняемые функции в обертке progn.
open - функция открывает filestream из filename и возвращает ссылку на поток
unwind-protect - специальный оператор, первую форму вычисляет как обычно, вторую - всегда, что бы ни случилось в первой(даже если какой exception или error вылез), эквивалентно блоку finally в жабе/сишарпе
close - освобождает дескриптор, как понятно из названия

anonymous
()
Ответ на: комментарий от anonymous

перед (let ...) бекквот, естественно: `(let ...)

anonymous
()
Ответ на: комментарий от anonymous

Мона так 

template <typename A, typename B, int (A::*init)(B&), int (A::*shutdown)(B&) > 
class T {

};

class A {
public:

	int foo(int&);
	int bar(int&);

};


T<A,int,&A::foo,&A::bar> obj;

Burbaka ★★
()
Ответ на: комментарий от Burbaka

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

В С++ локальная переменная типа класса влечёт вызов деструктора при выходе из области видимости. Так можно заменить любые вызовы unwind-protect (на котором основан, в частности, with-open-file).

Т.е., вместо любого конкретного применения with-open-file в С++ достаточно завести локальную переменную типа "поток". Поскольку в лиспе переменная всё равно нужна и она является локальной, такое использование в С++ превосходит with-open-file во всех отношениях.

Мораль: with-open-file и его аналоги в С++ просто не нужны.

Если же нужно что-то другое, то поставьте вопрос по-другому.

den73 ★★★★★
()
Ответ на: комментарий от den73

Похоже, я понял причину своего понимания. То, что Вы хотите - не аналогично with-open-file. With-open-file создаёт, открывает и даёт пользователю уже открытый поток. При этом поток никогда не виден пользователю в неоткрытом состоянии.

Вы же хотите сделать, по-сути, unwind-protect, трудность тут состоит в отсутствии в С++ и полноценных макросов, и полноценных замыканий.

Я бы взял внешний макропроцессор и сделал бы аналог unwind-protect с его помощью. m4 подойдёт. В этом случае не нужен будет класс, а можно будет подставлять в качестве init и shutdown произвольные куски кода, ссылающиеся на любые локальные переменные.

den73 ★★★★★
()
Ответ на: комментарий от den73

Ошибка вышла. Не получится с помощью внешнего макропроцессора. 
Оказывается, я недостаточно знаю С++. Я думал, что с помощью 
catch(...) можно перехватить и return, а, похоже, что нельзя. 

Единственное, если вместо return вызывать какой-то свой макрос, то 
можно сделать для C++ конструкцию unwind-protect, которая будет 
использоваться примерно так:

void process_session(Session &session,Data &data) {
  START_UNWIND_PROTECT(session.init(data)) {
    // работаем с открытым потоком
    if (error) RETURN(-1); // это не return, а наш макрос. 
  } ON_UNWIND {
    session.shutdown()
  } END_UNWIND_PROTECT
}
 
При этом используется механизм исключений. Если что-то перепутаем и 
забудем написать, вряд ли мы об этом узнаем на этапе компиляции. Хотя 
если поломать голову, то, может быть, и получится, особенно, если 
применять внешний макропроссор. 

den73 ★★★★★
()
Ответ на: комментарий от den73

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

{
with_active_object<FILE *,&fclose> f(fopen("aaaa.txt","r"));
// ... бла-бла-бла ...
}





Burbaka ★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.