LINUX.ORG.RU

C++ и указатели на методы


0

0

Итак, я хочу иметь клаcc op_table, содержащий таблицу указателей на функцию. Я определяю тип
typedef void (op_table::* op_func) ();

После этого я наследую от op_table новый класс pyxis_op_table, который уже будет содержать конкретные фунекции.

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

Объясните
1. Как работают указатели на члены класса;
2. Почему не работает мой код;
3. Как сделать то, что я хочу грамотно.

(... - пропущенный код)

cat op_table.hxx
#ifndef __op_table_hxx
#define __op_table_hxx

#include "../runtime.hxx"

namespace engine_stuff {

  struct op_table_exception {
    enum op_table_exception_type {
      index_out_of_range
    } type;

    op_table_exception (op_table_exception_type t): type (t)
    {}
  };

  class op_table {
  public:
    typedef int op_index;

    typedef void (op_table::* op_func) ();

  private:
    runtime * rt;

  protected:
    op_func * op_tbl;
    int size;

  public:
    op_table (runtime & r);
    virtual ~op_table ();

    void op (int index);
  };

};

#endif //__op_table_hxx


cat op_table.cc
#include "op_table.hxx"

engine_stuff::op_table::op_table (runtime & r): rt (&r)
{}

engine_stuff::op_table::~op_table ()
{}

void
engine_stuff::op_table::op (int index)
{
  if (index < 0 || index >= size)
    throw op_table_exception (op_table_exception::index_out_of_range);

  (this->*op_tbl[index]) ();
}


cat pyxis_op_table.hxx
#ifndef __pyxis_op_table
#define __pyxis_op_table

#include "op_table.hxx"

namespace engine_stuff {

  class pyxis_op_table: op_table {
  public:
    ...
    static const int op_push_const;
    ...

  private:
    ...
    void push_const ();
    ...

  public:
    pyxis_op_table (runtime & r);
    virtual ~pyxis_op_table ();
  };

};

#endif // __pyxis_op_table


cat pyxis_op_table.cc
#include "pyxis_op_table.hxx"

const int engine_stuff::pyxis_op_table::op_push_const =  9;

void
push_const ()
{
  ...
}

engine_stuff::pyxis_op_table::pyxis_op_table (runtime & r): op_table (r)
{
  op_tbl = new op_func[...];

  ...
  op_tbl[op_push_const] = pyxis_op_table::push_const;
  ...
}

engine_stuff::pyxis_op_table::~pyxis_op_table ()
{
  delete [] op_tbl;
}
anonymous

Ошибку выдает здесь

engine_stuff::pyxis_op_table::pyxis_op_table (runtime & r): op_table (r)
{
  op_tbl = new op_func[...];

  ...
  op_tbl[op_push_const] = pyxis_op_table::push_const; // !!! здесь ошибка !!!
  ...
}


pyxis_op_table.cc: In method `engine_stuff::pyxis_op_table::pyxis_op_table(engine_stuff::runtime &)':
pyxis_op_table.cc:22: assuming & on `engine_stuff::pyxis_op_table::push_const'
pyxis_op_table.cc:22: converting `void (engine_stuff::pyxis_op_table::*)()' to `void (engine_stuff::op_table::*)()' is a contravariance violation
pyxis_op_table.cc:22: conversion to `void (engine_stuff::op_table::*)()' from `void (engine_stuff::pyxis_op_table::*)()'
pyxis_op_table.cc:22: type `engine_stuff::pyxis_op_table' is not a base type for type `engine_stuff::op_table'
pyxis_op_table.cc:22:    in pointer to member conversion
make: *** [pyxis_op_table.o] Ошибка 1

anonymous
()

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

P.S.: если ты более-менее опишешь задачу, то может быть
я тебе даже подскажу, как ее надо решать :-)

P.P.S.: а обычно это делается так:

#include <stdio.h>
//
// Action processor - base class
//
class taction {
  protected:
    taction() {}
  public:
    virtual ~taction() {}
    virtual void do_it(const void * p) {}
};

//
// Implementation of action 1
//
class taction1 : public taction {
  public:
    taction1():taction() { }
    virtual void do_it(const void * p) { printf("Hi, %s!\n",p); }
};

//
// Implementation of action 2
//
class taction2 : public taction {
  public:
    taction2():taction() { }
    virtual void do_it(const void * p) { printf("Hello, %s!\n",p); }
};

//
// Engine
//
class engine {
  //
  // list of actions
  //
  taction * actions[2];

  public:
  //
  // register action for event with code "n"
  //
  void set_action(int n, taction * ac) { actions[n] = ac; }

  //
  // Execute action with code "number" and parameter "p"
  //
  void do_action(int number, const void * p) {
     actions[number]->do_it(p);
  }
};

int main() {
   taction * ac1 = new taction1();
   taction * ac2 = new taction2();
   engine * e = new engine();
   e->set_action(0,ac1);
   e->set_action(1,ac2);
   e->do_action(0,"world"); // prints "Hi, world!"
   e->do_action(1,"world"); // prints "Hello, world!"
   delete e;
   delete ac2;
   delete ac1;
}

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

Привет,
ничего плохого в
использывании указателей на functions нет.
Просто надо помнить о mangling (спроси у Сережи Брина)
Это именно то, о чем говорит kомпилятор.

Марк

carrot
()

Каждый метод должен содержать первым параметром указатель на this... а тебе лучше делать просто статические методы и сохранять указатели на них. Но опять же. Еще лучше пользоваться тем что тебе посоветовали из boost'а.

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

> ничего плохого в использывании указателей на functions нет

Если вам хочется искать проблемы на свою... хм... голову, то вперед - путь открыт. Можно передавать указатели на методы, можно передавать ссылки на объекты как void *, адрес виртуального метода можно прямо из VMT выдирать - ух сколько всего можно! Только потом не говорите, что вас не предупреждали!

Кроме того, при вызове нестатического метода объекта все равно предстоит тащить с собой ссылку на объект (которую нужно фактически нужно сдать первым параметром при вызове метода), так не проще ли не е&^%ть мозги и воспользоваться инструментом, разработанным специально для таких ситуаций?

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

[2 all] Я просто хотел сделать класс с таблицей операций для стековой
машины. А в производных классах иметь уже конкретные операции. Еще мне
надо, чтобы каждая операция имела доступ к указателю на класс runtime.
Я не хочу передавать его каждой функции или хранить в каждом классе
для операции (хотя наверное такая моя судьба, рас решил использовать
ООП (все чаще думаю, что зря... :( )). Пока отказался от базового
класса. То есть оставил просто pyxis_op_table.

[2 no-dashi] Во первых не указатели на функцию, а указатели на члены
класса. Они введены именно в C++ и не являются пережитком C. В книге
самого Страутструпа это дело иногда поощряется (как мне кажется, у меня
как раз такой случай) (Кстати, именно у Страутструпа я нашел, почему такое сделать нельзя)

[2 Carrot]
1. Что есть mangling ?
2. Я не знаю Сережу Брина (и если он появляется на этом форуме, не знаю
   его ник).

[2 eXOR] Статические методы не подойдут. Я уже говорил, что не хочу
каждой операции передавать указатель на runtime. А так я храню его в
в том же классе где методы (мне просто необходим this :) )

[2 no-dashi] Уже задумываюсь над инструментарием...

[2 All]
1. Что лучше использовать для довольно низкоуровневых и критичных к
   скорости вещей, которые нуждаются в быстром и качественном
   сопровождении и расширяемости...
2. Альтернатива ООП?
3. Пытаясь делать все так как принято в ООП, часто находишь
   противоречия, напиши я это в стиле C, а не создавая для этого тучу
   классов, все выглядело бы естественней и (главное) работало бы
   быстрее.

   По сему думаю (ИМХО), что C++ язык не для чистого ООП, а для более
   легкого управления теми проектами, которые должны быть написаны на
   чистом C. И возврат к истокам (к C) в некоторых местах вполне
   оправдан.

Прошу комментариев по всему этому, альтернативных решений и везких доводов :)
   C

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

> Во первых не указатели на функцию, а указатели на члены класса

Фиолетово :-) Реальности отличие состоит только в this, как вам уже сказали :-)

> Они введены именно в C++ и не являются пережитком C

Наверное, следовало бы добавить, что они необходимы в основном для плохо спроектированных программ... IMHO :-)

> Я просто хотел сделать класс с таблицей операций для стековой
> машины. А в производных классах иметь уже конкретные операции

Наследование, и еще раз наследование. Пример уже приводился.

> Еще мне надо, чтобы каждая операция имела доступ к указателю на класс runtime

class runtime { ...; static runtime * global_runtime; ... };
...
runtime::global_runtime = new runtime();
...
runtime::global_runtime->method();

??? :-)

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

anonymousу

1. Сережа Брин - автор http://www.google.com/. Про mangling -> к нему
2. "И возврат к истокам (к C)" -> посмотри src gdk -> там показано,
как можно ООП на C, наверное, это то, что тебе и надо ...

Марк

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