boost :: shared_ptr в модифицированном заводском методе

У меня есть собственная система IPC, основанная на сети (TCP-IP).

Рассмотрим код (и пояснение ниже):

#include "boost/shared_ptr.hpp"#include <string>

using namespace std;

class TCommand {
public:
typedef boost::shared_ptr<TCommand> Ptr;

TCommand() {
cout << "    Creating TCommand..." << endl;
}

virtual ~TCommand() {
cout << "    Destroying TCommand..." << endl;
}

static TCommand * factory(int classID);

virtual void parse(const char *data, int dataSize) = 0;
virtual void print() = 0;
virtual std::string getType() = 0;

};class TPingCommand : public TCommand {
public:
static const int classID = 1;
int value;

TPingCommand() : TCommand() {
cout << "    Creating TPingCommand..." << endl;
}

virtual ~TPingCommand() {
cout << "    Destroying TPingCommand..." << endl;
}

virtual void parse(const char *data, int dataSize) {
if (dataSize < 4) throw 1;

this->value = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3];
}

virtual void print() {
cout << "  TPingCommand:" << endl;
cout << "    value = " << dec << this->value << " (0x" << hex << this->value << ")" << endl;
}

virtual std::string getType() {
return "TPingCommand";
}
};

class TOtherCommand : public TCommand {
public:
static const int classID = 2;
int value;
char value2;
short int value3;

TOtherCommand() : TCommand() {
cout << "    Creating TOtherCommand..." << endl;
}

virtual ~TOtherCommand() {
cout << "    Destroying TOtherCommand..." << endl;
}

virtual void parse(const char *data, int dataSize) {
if (dataSize < 7) throw 1;

this->value = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3];
this->value2 = data[4];
this->value3 = data[5] << 8 | data[6];
}

virtual void print() {
cout << "  TOtherCommand:" << endl;
cout << "    value  = " << dec << this->value << " (0x" << hex << this->value << ")" << endl;
cout << "    value2 = " << dec << this->value2 << " (0x" << hex << (int)this->value2 << ")" << endl;
cout << "    value3 = " << dec << this->value3 << " (0x" << hex << this->value3 << ")" << endl;
}

virtual std::string getType() {
return "TOtherCommand";
}
};TCommand * TCommand::factory(int classID) {
cout << "  Factory for classID = " << dec << classID << " (0x" << hex << classID << ")" << endl;
switch (classID) {
case TPingCommand::classID: return new TPingCommand(); break;
case TOtherCommand::classID: return new TOtherCommand(); break;
default: throw 1;
}
}TCommand::Ptr receiveFromNetwork(int test, TCommand::Ptr knownCommand)
{
// Receive command header from network.
// int classID is the command class internal ID.
// int dataSize is the command's body size in bytes.
// For instance:
//   int classId = 2;
//   int datasize = 7;

int classId = 1;
int dataSize = 4;
char data[10];

if (test == 0) {
cout << "  Using test data 0..." << endl;
classId = 1;
dataSize = 4;
data[0] = 0x01; data[1] = 0x02; data[2] = 0x03; data[3] = 0x04;
} else if (test == 1) {
cout << "  Using test data 1..." << endl;
classId = 2;
dataSize = 7;
data[0] = 0x11; data[1] = 0x12; data[2] = 0x13; data[3] = 0x14; data[4] = 0x41; data[5] = 0x16; data[6] = 0x17;
}

TCommand::Ptr cmd;
if (knownCommand == 0) {
cout << "  No command provided." << endl;
cmd.reset(TCommand::factory(classId));
cout << "  Command created from factory: " << cmd->getType() << endl;
} else {
cmd = knownCommand;
cout << "  Command provided: " << cmd->getType() << endl;
}

cout << "  Parsing data..." << endl;
cmd->parse(data, dataSize);

// The command was identified as TOtherCommand (classID = 2).
// The factory returned a TOtherCommand instance.
// The TOtherCommand's parse method will check the dataSize is suitable (7 bytes are necessary).
// The parse method will unserialize data to their fields.
// This way, the fields would be:
//    data = 0x11121314;
//    data2 = 0x42; // 'A' as char.
//    data3 = 0x1213;

return cmd;
}

void caller() {
// Case 1 (ok):
// I know I'm going to receive a TPingCommand.
cout << "Test case 1:" << endl;
TCommand::Ptr known(new TPingCommand());
TCommand::Ptr cmd1 = receiveFromNetwork(0, known);
cmd1->print();

// Case 2 (problems):
cout << "Test case 2:" << endl;
TCommand::Ptr dummy;
TCommand::Ptr cmd2 = receiveFromNetwork(1, dummy);
cmd2->print();

cout << "Teardown..." << endl;
}

int main() {
caller();
}

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

Во всех остальных случаях первые несколько байтов, полученных из сети, описывают команду classID, и я использую ее для создания подходящего экземпляра TCommand внутри этой функции. Затем команда анализируется из сетевых данных и возвращается в конце функции. KnownCommand — просто фиктивный экземпляр TCommand.

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

Я думал об использовании ссылки TCommand для knownCommand, однако я не могу этого сделать, потому что мне придется возвращать общий указатель, и это приведет к тому, что один и тот же необработанный указатель будет управляться двумя разными экземплярами общего указателя (один из вызывающей стороны). метод и другой внутри метода receiveFromNetwork).

У кого-нибудь есть хорошая идея, как это решить?

Вот частичный вывод valgrind проблемного сценария:

==31859== Thread 2:
==31859== Invalid read of size 4
==31859==    at 0x805D7B0: boost::detail::sp_counted_impl_p<TVideoGetSourceSizeCommand>::dispose() (checked_delete.hpp:34)
==31859==    by 0x407FF42: Server::receiveFromNetwork() (sp_counted_base_gcc_x86.hpp:145)
==31859==    by 0x40800AF: Server::serverThread(void*) (Server.cpp:107)
==31859==    by 0x434496D: start_thread (pthread_create.c:300)
==31859==    by 0x42B398D: clone (clone.S:130)

Спасибо большое.

0

Решение

Нашел проблему.

В вызывающей части кода shared_ptr «дублировался» неправильным образом.
В результате ошибка была примерно такой:

boost::shared_ptr<DerivedType> ptr1(new DerivedType(...));
boost::shared_ptr<BaseType> ptr2(ptr1.get());

Конечно, это вызвало дублирование БЕСПЛАТНО и сбой программы.

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

Спасибо за всю помощь, ребята.

0

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]