Модераторы: Daevaorn

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Как передать функтор в метод ждущий указатель? Неужели только static-методом? 
:(
    Опции темы
EvilsInterrupt
Дата 7.12.2012, 10:42 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Executables research
***


Профиль
Группа: Завсегдатай
Сообщений: 1019
Регистрация: 14.7.2007
Где: Железнодорожный, МО, Россия

Репутация: 2
Всего: 9



Есть код:
Код

typedef   bool ( *ErrorHadler_type )( const std::string & filename, int code );


class ErrorHandler
{
public:

    bool operator()( const char * errText, int errCode )
    {
        errors.push_back( ErrorInfo(errText,errCode) );
        return false;
    }

    int code() const
    {
        ErrorsList_t::const_iterator  errIter = errors.begin();
        ErrorsList_t::const_iterator  errEnd = errors.end();

        int code = 0;
        while( errIter != errEnd )
        {
            if( errIter->code > code )
            {
                code = errIter->code;
            }
            ++errIter;
        }
        return code;
    }

private:

    struct ErrorInfo
    {
        const char * text;
        int code;

        ErrorInfo( const char * errText, int errCode )
            : text( errText )
            , code( errCode )
        {
        }
    };

    typedef  std::list< ErrorInfo >   ErrorsList_t;

    ErrorsList_t  errors;
};


class Parser
{
public:
    explicit Parser( ErrorHadler_type errHandler )
        : handler( errHandler )
    {
    }

    void method1()
    {
        handler( "method1()", 3 );
    }

    void method2()
    {
        handler( "method2()", 4 );
    }

private:
    ErrorHadler_type  handler;
};


int main( int argc, char* argv[] )
{
    ErrorHandler errHandler;
    Parser parser1( errHandler );    // FAIL

    /*
    errHandler("Proba 1", 1 ); // OK
    errHandler("Proba 2", 2 ); // OK
    */

    std::cout << "Error code : " << errHandler.code() << std::endl;

    return EXIT_SUCCESS;
}


Я ожидаю что передавая объект, автоматически вызовется operator(). Мне нужно чтобы класс парсера вызвал этот оператор, а тот в свою очередь поменял состояние объекта,запомнив переданную ошибку.

Прав ли я? Или все не так просто и функтор не может менять состояние объекта? Если да, то мне похоже нужно юзать static метод, а в нем писать либо статическую переменную или обращение к синглтону или другую магию...
PM MAIL WWW ICQ Jabber   Вверх
volatile
Дата 7.12.2012, 12:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2107
Регистрация: 7.1.2011

Репутация: 37
Всего: 85



Цитата(EvilsInterrupt @  7.12.2012,  10:42 Найти цитируемый пост)
функтор не может менять состояние объекта?

Здесь это не причем. 
Вы передаете ему копию объекта, вот состояние этой копии он и меняет.
Здесь 2 пути. либо передавать ему ссылку на объект, либо копию объекта хранящуюся в парсере считать основной.
Выбор способа зависит от логики вашей программы (чтото здесь советовать трудно, т.к. мало информации.)
1-ый способ:
Цитата(EvilsInterrupt @  7.12.2012,  10:42 Найти цитируемый пост)
class Parser
{
public:
    explicit Parser( ErrorHadler & errHandler )
        : handler( errHandler )
    {
    }
    ...
    ErrorHadler & handler;
};


2-ой способ:
Цитата(EvilsInterrupt @  7.12.2012,  10:42 Найти цитируемый пост)
  std::cout << "Error code : " << parser1.errHandler.code() << std::endl;

Во 2-ом случае, нужно будет дать доступ к объекту Parser::errHandler, либо геттером, либо явно, переместив его в паблик.
 



Это сообщение отредактировал(а) volatile - 7.12.2012, 12:17
PM MAIL   Вверх
volatile
Дата 7.12.2012, 12:33 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2107
Регистрация: 7.1.2011

Репутация: 37
Всего: 85



Цитата(EvilsInterrupt @  7.12.2012,  10:42 Найти цитируемый пост)
typedef   bool ( *ErrorHadler_type )( const std::string & filename, int code );

Я не заметил что у вас в парсере хранится не объект, а указатель на функцию. 
Зачем?
operator(), имеет совсем другую сигнатуру.
вам нужно хранить либо указатель на сам объект ErrorHadler, либо ссылку на него. (что я и предложил в 1-ом способе)

PM MAIL   Вверх
volatile
Дата 7.12.2012, 12:51 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2107
Регистрация: 7.1.2011

Репутация: 37
Всего: 85



Еще у вас опечатка
Цитата(EvilsInterrupt @  7.12.2012,  10:42 Найти цитируемый пост)
ErrorHandler


Короче вот весь текст целиком : smile 
Код

class ErrorHandler
{
public:

    bool operator()( const char * errText, int errCode )
    {
        errors.push_back( ErrorInfo(errText,errCode) );
        return false;
    }

    int code() const
    {
        ErrorsList_t::const_iterator  errIter = errors.begin();
        ErrorsList_t::const_iterator  errEnd = errors.end();

        int code = 0;
        while( errIter != errEnd )
        {
            if( errIter->code > code )
            {
                code = errIter->code;
            }
            ++errIter;
        }
        return code;
    }

private:

    struct ErrorInfo
    {
        const char * text;
        int code;

        ErrorInfo( const char * errText, int errCode )
            : text( errText )
            , code( errCode )
        {
        }
    };

    typedef  std::list< ErrorInfo >   ErrorsList_t;

    ErrorsList_t  errors;
};


class Parser
{
public:
    explicit Parser( ErrorHandler & errHandler )
        : handler( errHandler )
    {
    }

    void method1()
    {
        handler( "method1()", 3 );
    }

    void method2()
    {
        handler( "method2()", 4 );
    }

private:
    ErrorHandler &  handler;
};


int main( int argc, char* argv[] )
{
    ErrorHandler errHandler;
    Parser parser1( errHandler );    // ОК

    parser1.method1();
    std::cout << "Error code : " << errHandler.code() << std::endl;

    return EXIT_SUCCESS;
}


Это сообщение отредактировал(а) volatile - 7.12.2012, 12:53
PM MAIL   Вверх
EvilsInterrupt
Дата 7.12.2012, 15:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Executables research
***


Профиль
Группа: Завсегдатай
Сообщений: 1019
Регистрация: 14.7.2007
Где: Железнодорожный, МО, Россия

Репутация: 2
Всего: 9



volatile
Ок, спасибо!

Цитата

Я не заметил что у вас в парсере хранится не объект, а указатель на функцию. 
Зачем?

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

Если точнее сформулировать требования к этой ситуации, то нужно уметь передавать :
1) Как указатель на обычную функцию
2) Так и внешне созданный объект класса с переопределенным operator(), который позволит удобным способом накапливать ошибки

Пока склоняюсь к шаблонному решению,на подобии того что сделано в std::for_each.

Код

template<typename Object, typename Operator>
std::for_each( ...., Operator op )
{
// more code

PM MAIL WWW ICQ Jabber   Вверх
mes
Дата 7.12.2012, 20:03 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



EvilsInterrupt
boost::function в комплекте с boost::bind сделают мечты реальностью ))
для С++11 и буста не надо..все  в коробке))


--------------------
PM MAIL WWW   Вверх
EvilsInterrupt
Дата 8.12.2012, 10:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Executables research
***


Профиль
Группа: Завсегдатай
Сообщений: 1019
Регистрация: 14.7.2007
Где: Железнодорожный, МО, Россия

Репутация: 2
Всего: 9



mes
Полностью согласен по поводу буста, в своих проектах(домашних) применяю его и по поводу новых технологий в виде С++11 тоже согласен, но не всегда можно применить все что душе хочется :(

Попробую сформулировать проблему еще раз, а то кажется меня не понимают ;)

Вообщем мне надо на обычном С++03 мою проблему решить, т.к. он MSVS 2008.

На псевдокоде то что я хочу получить выглядит примерно так:

Код

typedef   bool ( *ErrorHandlerPtr_type )( const char * filename, int code );

class ErrorHandler
{
public:

    bool operator()( const char * errText, int errCode )
    {
        errors.push_back( ErrorInfo(errText,errCode) );
        return false;
    }

    int code() const
    {
        ErrorsList_t::const_iterator  errIter = errors.begin();
        ErrorsList_t::const_iterator  errEnd = errors.end();
        int code = 0;
        while( errIter != errEnd )
        {
            if( errIter->code > code )
            {
                code = errIter->code;
            }
            ++errIter;
        }
        return code;
    }

private:
    struct ErrorInfo
    {
        const char * text;
        int code;
        ErrorInfo( const char * errText, int errCode )
            : text( errText )
            , code( errCode )
        {
        }
    };
    typedef  std::list< ErrorInfo >   ErrorsList_t;
    ErrorsList_t  errors;
};


template< typename ErrorHandler_type = ErrorHandlerPtr_type >
class Parser
{
public:
    explicit Parser( ErrorHandler_type & errHandler = defErrorHandler )
        : handler( errHandler )
    {
    }

    void method1()
    {
        handler( "method1()", 3 );
    }

    void method2()
    {
        handler( "method2()", 4 );
    }

    bool defErrorHandler( const char * errTxt, int errCode )
    {
        return true;
    }
private:
    ErrorHandler_type & handler;
};


int main( int argc, char* argv[] )
{
    ErrorHandler errHandler;
    Parser parser1( errHandler );    // FAIL

    parser1.method1();
    parser1.method2();

    std::cout << "Error code : " << errHandler.code() << std::endl;
    return EXIT_SUCCESS;
}


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

Возникает вопрос:

Как имея:
1) Обычные функции подобные defErrorHandler
2) Имея класс функтор

Подавать в explicit-конструктор класса парсера как п.1 так и п.2 ? При этом текущие тулзы имеют семантику использования парсера в таком виде: Parser parser1(), т.е. обработчик по умолчанию.
PM MAIL WWW ICQ Jabber   Вверх
volatile
Дата 8.12.2012, 18:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2107
Регистрация: 7.1.2011

Репутация: 37
Всего: 85



EvilsInterrupt, а что запрещает буст::функшен использовать? там действительно все очень удобно.

Кроме-того у вас помимо основной проблемы, еще много "шума".

Цитата(EvilsInterrupt @  8.12.2012,  10:55 Найти цитируемый пост)
 Обычные функции подобные defErrorHandler

Цитата(EvilsInterrupt @  8.12.2012,  10:55 Найти цитируемый пост)
  bool defErrorHandler( const char * errTxt, int errCode )
    {
        return true;
    }

defErrorHandler - вовсе не обычная функция, это функция-мембер. а это 2 большие разницы.

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

Цитата(EvilsInterrupt @  8.12.2012,  10:55 Найти цитируемый пост)
Как имея:
1) Обычные функции подобные defErrorHandler
2) Имея класс функтор
Подавать в explicit-конструктор класса парсера как п.1 так и п.2 ?


Если обычная функция, это действительно обычная функция,
а передача функтора в виде ссылки на существующий "объект с состоянием"- требование, 
то можно сделать так:

Код

typedef bool (*func_type) (const std::string &, int);

// Обычная функция (дефолтный обработчик)
bool default_err_fun (const std::string & filename, int code) 
{
   std::cout << "Default plain function" << std::endl;
   return 0;
}

class ErrorHandler_base
{
public:
   ErrorHandler_base (func_type f = default_err_fun) : f_(f) {}
   virtual ~ ErrorHandler_base () {};

   virtual bool operator() (const std::string & errText, int errCode)
       { return f_ (errText, errCode); }
private:
   func_type f_;
};
//-------------------------------------------------------------------
class Parser
{
public:
    explicit Parser (ErrorHandler_base & h)
       : handler (h) {}

    explicit Parser (func_type f = default_err_fun)
       : def_handler (f), handler (def_handler) {}

    void method () { handler ("method1", 42); }

private:
    ErrorHandler_base def_handler;
    ErrorHandler_base & handler;
};

//-------------------------------------------------------------------
// Свой класс обработчик ошибок. (Требование : отнаследоваться от ErrorHandler_base)
class ErrorHandler : public ErrorHandler_base
{
public:
    bool operator()( const std::string & errText, int errCode )
    {
        code_ = errCode;
        std::cout << "ErrorHandler operator()" << std::endl;
        return 0;
    }

    int code () const { return code_; }
    // ...

private:
    int code_;
    // ...
};

// Своя обычная функция обработчик ошибок.
bool my_err_fun (const std::string & filename, int code)
{
   std::cout << "My plain function" << std::endl;
   return 0;
}


int main( int argc, char* argv[] )
{
    Parser parser_1;                // Создание парсера с дефолтным обработчиком
    parser_1.method ();

    Parser parser_2 (my_err_fun);   // Создание парсера со своей простой функцией-обработчиком
    parser_2.method();
    
    ErrorHandler errHandler;        
    Parser parser_3 (errHandler);   // Создание парсера со своим объектом-обработчиком,   
    parser_3.method ();

    std::cout << "--------------------\nError code : " << errHandler.code() << std::endl;
}


Читайте комменты в коде.
(если бы не вышеизложенные требования, то можно было бы проще)

Добавлено через 1 минуту и 9 секунд
Если опять не то, то постарайтесь упростить код вопроса, убрав все лишнее.
Как я уже говорил, у вас много лишнего "шума", причем не понятно, что из этого требование, а что случайный/ошибочный код.

PM MAIL   Вверх
volatile
Дата 8.12.2012, 18:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2107
Регистрация: 7.1.2011

Репутация: 37
Всего: 85



Да, и забыл привести вывод:
Код

Default plain function
My plain function
ErrorHandler operator()
--------------------
Error code : 42


Если обычная функция, это не обычная функция smile , то есть она должна быть мембером Parser'а, то это тоже можно сделать.

PM MAIL   Вверх
EvilsInterrupt
Дата 8.12.2012, 23:03 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Executables research
***


Профиль
Группа: Завсегдатай
Сообщений: 1019
Регистрация: 14.7.2007
Где: Железнодорожный, МО, Россия

Репутация: 2
Всего: 9



volatile
Цитата

(если бы не вышеизложенные требования, то можно было бы проще)

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

Спасибо за внимание, ну и раз Вы уж упомянули "было бы проще". Хотелось бы Ваше проектное решение увидеть,если конечно не затруднит )
PM MAIL WWW ICQ Jabber   Вверх
volatile
Дата 8.12.2012, 23:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2107
Регистрация: 7.1.2011

Репутация: 37
Всего: 85



EvilsInterrupt, ну это подошло или нет?
PM MAIL   Вверх
EvilsInterrupt
Дата 8.12.2012, 23:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Executables research
***


Профиль
Группа: Завсегдатай
Сообщений: 1019
Регистрация: 14.7.2007
Где: Железнодорожный, МО, Россия

Репутация: 2
Всего: 9



volatile
Да, Спасибо! Пока не вижу ничего чтобы сказать "не подходит".
Все-таки хотелось бы увидеть решение более опытного товарища ;)
PM MAIL WWW ICQ Jabber   Вверх
mes
Дата 8.12.2012, 23:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Цитата(EvilsInterrupt @  8.12.2012,  09:55 Найти цитируемый пост)
Подавать в explicit-конструктор класса парсера

вот  скажите причем тут ехплицит  ? зачем отвлекаться на несущественные детали ?

Добавлено через 3 минуты и 13 секунд
Цитата(EvilsInterrupt @  8.12.2012,  09:55 Найти цитируемый пост)
но не всегда можно применить все что душе хочется 

если готовое использовать не получается попробую показать ход мыслей, для решения подобных задач ))




--------------------
PM MAIL WWW   Вверх
mes
Дата 9.12.2012, 00:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Цитата(EvilsInterrupt @  8.12.2012,  09:55 Найти цитируемый пост)
Как имея:
1) Обычные функции подобные defErrorHandler
2) Имея класс функтор

итак мы должны для одного и то же действия использовать полиморфизм, при этом, забегая вперед, имеются, как минимум, две возможности.. посредством виртуальных функций или "привязыванием", но для общего представления это не важно..  
также, нам нужно это сделать, чтоб для пользователя было прозрачным..  значит нужен враппер, и так как враппер долже иметь состояние, значит используем функтор.. 
итого две подзадачи.. 
1. адаптация функции/функтора 
2. сокрытие под удобным интерфейсом




--------------------
PM MAIL WWW   Вверх
mes
Дата 9.12.2012, 00:51 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



допустим интерфейс имеет такую структуру
Код

struct iface
{
  virtual bool do_some (const std::string&) =0;
};

тогда нам нужна пару адаптеров, один для функции другой для функтора..

Код

typedef bool(free_fn)(const std::string&); 
struct fn_adapter : iface 

   virtual bool do_some (const std::string& str)   
   { 
      return  _fn(str);   
   } 
   fn_adapter(free_fn *fn) : _fn(fn)   {   }  

   free_fn * _fn;    
}; 

iface * create_iface(free_fn * fn) {   return new fn_adapter(fn);  } 


для функтора аналогично..

Добавлено через 9 минут и 43 секунды
теперь обернем интерфейс
Код

struct func
{

  //для функции
  func(free_fn * fn ) 
  {
      _iface =  create_iface(fn);
  }
  //для функтора
  func (.. )
  {
     ..
  }

  bool operator()(const std::string& str)
  {
     return _iface->do_some(str);
  }  
  iface * _iface;
};



--------------------
PM MAIL WWW   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
Earnest Daevaorn

Добро пожаловать!

  • Черновик стандарта C++ (за октябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика(4.4мб).
  • Черновик стандарта C (за сентябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика (3.4мб).
  • Прежде чем задать вопрос, прочтите это и/или это!
  • Здесь хранится весь мировой запас ссылок на документы, связанные с C++ :)
  • Не брезгуйте пользоваться тегами [code=cpp][/code].
  • Пожалуйста, не просите написать за вас программы в этом разделе - для этого существует "Центр Помощи".
  • C++ FAQ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn

 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.1230 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.