LINUX.ORG.RU

[C++] Туплю :(

 


0

1

Всем привет.

Есть довольно простая проблемка, но что-то клинит меня после праздников. Имею: надо передавать, скажем, 20 типов сообщений между двумя программами. Для каждого сообщения создам класс, в котором будет поле struct Message, которое для каждого класса содержит так сказать уникальные данные, которые будут посылаться и приниматься. В каждом классе есть 2 метода, которые существуют в каждом классе и в каждом классе абсолютно одинаковы, но работают, естесственно с данными своего класса. Что-то типа:

class CStartStatus : public CBaseIpcMessage
{
public:
enum Status
{
START_OK = 0,
START_PROBLEM_MOUNT_CONF_DATA,
START_PROBLEM_TOUCH_SCREEN,
};

struct Message
{
enum MsgId id;
enum Status status;
};

public:
CStartStatus();
void* getMesDump( int& len );
eError decodeMessage( struct nlmsghdr *nlh );

protected:
struct Message m_message;
}




////////////////////////////////////////////////////
CStartStatus::CStartStatus()
{
m_message.id = ID_IPC_START_STATUS;
m_message.status = START_OK;
}


////////////////////////////////////////////////////
void* CStartStatus::getMesDump( int& len )
{
len = sizeof( m_message );
return &m_message;
}


////////////////////////////////////////////////////
eError CStartStatus::decodeMessage( struct nlmsghdr *nlh )
{
if( nlh->nlmsg_len - sizeof( nlmsghdr ) != sizeof( m_message ) )
{
return ERR_WRONG_REC_MES_LENGTH;
}

memcpy( &m_message, NLMSG_DATA( nlh ), sizeof( m_message ) );
return ERR_OK;
}


Подскажите, как сделать так, чтоб getMesDump() и decodeMessage() объявить один раз и не такскать из класса в класс. Просто объявить в базовом классе не получится, поскольку методы работают с данными известными только в производных классах...


Всем спасибо
Velik


Ты хочешь friend функцию?

Deleted
()

эм, для начала, плиз, осильте теги, и в частности


[quote]Подскажите, как сделать так, чтоб getMesDump() и decodeMessage() объявить один раз и не такскать из класса в класс.[/quote]

1) подпихните в шаблонные параметры[br]
2) используйте наследование

[quote]Просто объявить в базовом классе не получится, поскольку методы работают с данными известными только в производных классах...
[/quote]

ну и передавайте данные в метод как параметры, там у Вас немного данных

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

Ок, почитаю.

сделай getMesDump() виртуальной

А толку? В базовом она и так объявлена как виртуальная, но базовый не знает данные в производном классе.


Ты хочешь friend функцию?

А как она тут поможет?

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

> А толку? В базовом она и так объявлена как виртуальная, но базовый не знает данные в производном классе.

ппц… что тебе нужно в decodeMessage()? указатель на данные и их размер, разве нет? всё это даёт getMesDump(). сделай в базовом классе getMesDump() чистой виртуальной и там же реализуй decodeMessage(), использующую getMesDump() для получения инфы о сообщении.

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

сделай в базовом классе getMesDump() чистой виртуальной

При этом её прийдётся реализовывать (так как есть и сейчас) в каждом производном классе. Т.е. таким способом избавлюсь только от реализации decodeMessage() в каждом производном. Согласны?

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

> Согласны?

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

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

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

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

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

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

Это, как мне кажется, не сработает. Поскольку в базовом указатель на пакет пришлось бы хранить как void*. А чтоб работать с указателем на void, его пришлось бы привести к типу данных известному только в производном классе. Как ни крути, а не могу придумать, как обойтись вообще описав только один раз где-нибудь в базовом. Кстати, не сработает так просто и с шаблонами, как товарищ выше предложил. Можно в базовом определить что-то вроде

    template<typename T> eError dec( struct nlmsghdr *nlh, T* mes )
    {
        if( nlh->nlmsg_len - sizeof( nlmsghdr ) != sizeof( *mes ) )
        {
            return ERR_WRONG_REC_MES_LENGTH;
        }

        memcpy( mes, NLMSG_DATA( nlh ), sizeof( *mes ) );
        return ERR_OK;
    }

Но потом в КАЖДОМ производном пришлось бы определять что-то вроде


    eError decodeMessage( struct nlmsghdr *nlh )
    {
        dec( nlh, &m_message );
        return ERR_OK;
    }

Меня не покидает странное ощущение, шо я туплю не па-децки :)

velikS
() автор топика
Ответ на: комментарий от Tark

а если поле меняется, то нужна будет конкретная функция для излечения этих данных из нужного поля

Таки да. Но при этом все функции извлечения данных из поля производных объектов 100% идентичны между собой, только работают с полями известными только в производных классах. Не хочется делать copy&paste в каждом производном

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

Я не особо силен в архитектуре таких приложений, но почему бы не делать просто ссылку m_message на нужное поле?

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

> Это, как мне кажется, не сработает. Поскольку в базовом указатель на пакет пришлось бы хранить как void*. А чтоб работать с указателем на void, его пришлось бы привести к типу данных известному только в производном классе.

давай ты поспишь (да и я тоже), а утром таки расскажешь, где здесь void* приводится «к типу данных известному только в производном классе» (не забываем, что я также говорил о том, что размер данных хранится в отдельном поле базового класса, а значит sizeof() делать уже не нужно):

void* CStartStatus::getMesDump( int& len )
{
	len = sizeof( m_message );
	return &m_message;
}

eError CStartStatus::decodeMessage( struct nlmsghdr *nlh )
{
	if( nlh->nlmsg_len - sizeof( nlmsghdr ) != sizeof( m_message ) )
	{
		return ERR_WRONG_REC_MES_LENGTH;
	}

	memcpy( &m_message, NLMSG_DATA( nlh ), sizeof( m_message ) );
	return ERR_OK;
}
arsi ★★★★★
()

не читал весть тред, добавлю словечко:
можно сделать просто еще один класс, потом просто его наследовать там где необходимо

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

>А как она тут поможет?

Ах, да. От того, что тебе мешает это не избавляет.

// А вот в некоторых динамических языках этой проблемы нет.

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

а утром таки расскажешь, где здесь void* приводится «к типу данных известному только в производном классе»


Я не стал отвечать, потому что таки да - стоило поспать. Не скажу, что выспался, но будильник...

Я думаю, что здесь вы правы - в базовом можно было бы определить указатель на данные (а он будет void* !) и поле размера данных.Если базовый будет знать размер данных, то приводить void* к другому типу данных уже не потребуется. Т.е. вроде бы как задача решена - объявили указатель на данные и размер данных в базовом, заполняем всё это в конструкторах производных классов, каждый из которых знает свой struct Message. НО! В [b]КАЖДОМ[/b] производном опять придётся копипастить инициализацию «наших» полей. Т.е. вообще в сухую задачу не реализовать.

Меня всё это волнует по одной простой причине - сообщений (со своими классами реализации) будет десятка 2. Я сейчас могу сделать всё, что хочу, но код должен быть с простым и надёжным к добавлению новых сообщений.К примеру, понадобится через год другому человечку добавить новое сообщение - он вставил класс реализации в хеадер со своей struct Message и всё автоматически заработало с поддержкой нового сообщения.А так 100% этот Хер забудет чё-нить добавить в конструкторе и ищи пляши. Нет, понятно, что указатель и размер поля в базовом конструкторе будут инициализированы в 0 и будут проверяться при доступе в decodeMessage() и getMesDump() методах. Но сама идея тут такова - что вообще без куска кода в производном классе не обойтись.

Вот такие мысли за чаем :)

velikS
() автор топика
Ответ на: комментарий от Tark

Я не особо силен в архитектуре таких приложений, но почему бы не делать просто ссылку m_message на нужное поле?

Да я сам тут, похоже, сильно плаваю. Это как раз то, что и предлагает arsi.

Вы были очень правы в своём предыдущем посте. В C++, похоже, дыра в том месте, что он не работает с абстрактным типом данных, сам определяя, что это за данные (кстати, делает ли это какой другой язык?) В С++ нужно или реализовать обработку данных по шаблону, а потом в каждом производном классе (который знает свои данные) подсовывать тип данных или сделать так, как предлагает arsi: универсальный указатель и размер данных сохраняются в базовом, а заполняются в производных

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

> А так 100% этот Хер забудет чё-нить добавить в конструкторе и ищи пляши.

сделай в базовом классе единственный конструктор CBaseIpcMessage(void *ptr, size_t size), который будет заполнять необходимые поля. так что не будет другого выхода, как вызвать базовый конструктор из конструктора производного класса:

CStartStatus::CStartStatus() : CBaseIpcMessage(&m_message, sizeof m_message)
{
	m_message.id = ID_IPC_START_STATUS;
	m_message.status = START_OK;
}
arsi ★★★★★
()
Ответ на: комментарий от arsi

Еще раз привет.

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

Еще раз спасибо за обсуждение
velik

velikS
() автор топика

> Есть довольно простая проблемка, но что-то клинит меня после праздников.

а что за праздники-то были? о_О

Obey-Kun ★★★★★
()
Ответ на: комментарий от velikS

сделайте в базовом классе вирутальную функцию get, ее и используйте вместо поля, а в наследниках заимплементь с возвратом нужного данных, если я правильно понял прблему

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

А, точно, Пасха же (и Пейсах). Не, религиозные праздники не отмечаю. Ещё ж куличи и яйца крашенные продают вокруг, а я как-то внимания не обратил :).

Obey-Kun ★★★★★
()
Ответ на: комментарий от JFreeM

сделайте в базовом классе вирутальную функцию get, ее и используйте вместо поля, а в наследниках заимплементь с возвратом нужного данных, если я правильно понял прблему


Проблема в том, что в КАЖДОМ производном классе КАЖДАЯ такая get идентична. Т.е. имеем тупой copy & paste сщ всеми выткающими. Я уже решил и забыл это дело (см. выше)


А, точно, Пасха же (и Пейсах). Не, религиозные праздники не отмечаю. Ещё ж куличи и яйца крашенные продают вокруг, а я как-то внимания не обратил :)


Да я и сам не отмечаю, но выходные пришлись на пятницу и понедельник (соответственно и на субботу с воскресеньем ;-) ) и оставили свой хмельной отпечаток :)

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

>Проблема в том, что в КАЖДОМ производном классе КАЖДАЯ такая get идентична. Т.е. имеем тупой copy & paste сщ всеми выткающими. Я уже решил и забыл это дело (см. выше)

тогда почему бы не перенести поля в суперкласс и не использовать его? я что-то не пойму.

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

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

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

>Проблема в том, что в КАЖДОМ производном классе КАЖДАЯ такая get идентична.

Так прикол в том, что все поля объявлены в производных классах, т.е. там, где им и место.


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

ПС я совершенно не разбираюсь в ньюансах си++, говорю с точки зрения джависта.

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