Создать пользовательское расширение из функции для std :: vector - PullRequest
0 голосов
/ 16 октября 2019

Я пытаюсь изучить C ++ из C #. В C # мне нравились расширения типа CustomItem.ToString(), и мне было любопытно, как я могу реализовать что-то подобное в C ++. Я использую std::vector<unsigned char> для хранения буфера и затем обрабатываю его побайтово.

У меня есть следующая функция:

int DataParser::ToLittleEndianInt(std::vector<unsigned char> ba) //Little Endian
{
    long int Int = 0;
    int arraySize = ba.size();
    if (arraySize ==4)
    {
        Int = ba[0] | ((int)ba[1] << 8) | ((int)ba[2] << 16) | ((int)ba[3] << 24);
    }
    else if (arraySize == 2)
    {
        Int = ba[0] | ((int)ba[1] << 8);
    }
    else if (arraySize == 1)
    {
        Int = ba[0];
    }
    return Int;
}

Здесь я могу отправить ему вектор от одного до 4 байтов, и он будет преобразован в целое число для меня. Есть ли способ для меня использовать его таким образом:

std::vector<unsigned char> CurrentBytes(4);
for (int i = 0; i < 4; i++)
    CurrentBytes[i]=1;

// how can we do this?
int results = CurrentBytes.ToLittleEndianInt();
//or
int results = CurrentBytes->ToLittleEndianInt();

Я просто чувствую, что это вполне читабельно и хочу иметь расширения для строк, дат, int, долларов и т. Д.

ОБНОВЛЕНИЕ:

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

class Byte
{
    public:
        Byte();
        ~Byte();
        std::string  DataType;
        int  ColumnWidth;
        std::vector<unsigned char> data;
        int ToLEInt() {
            long int Int = 0;
            int arraySize = this->ColumnWidth;
            if (arraySize == 2)
            {
                Int = this->data[0] | ((int)this->data[1] << 8);
            }
            else if (arraySize == 1)
            {
                Int = this->data[0];
            }
            return Int;
        };
};

Это выдает ошибку:

Error   LNK2019 unresolved external symbol "public: __thiscall Byte::Byte(void)" (??0Byte@@QAE@XZ) referenced in function "public: static void __cdecl DataParser::Parse(class nlohmann::basic_json<class std::map,class std::vector,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,bool,__int64,unsigned __int64,double,class std::allocator,struct nlohmann::adl_serializer>)" (?ParseToDataTable@Parse@@SAXV?$basic_json@Vmap@std@@Vvector@2@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@2@_N_J_KNVallocator@2@Uadl_serializer@nlohmann@@@nlohmann@@@Z)    

Я также попытался объявить это как:

const Byte& ToLEInt(const Byte& s)const {}

Это былокак я видел в Стивене Прате, написанном на C ++ Primer, но я все равно получаю ошибки, и мне придется использовать это в моем cpp:

std::vector<unsigned char> test(3);
for (int i = 0; i < 2; i++)
        test[i] = i;
    Byte CurrentBytes;
    CurrentBytes.data = test;
    CurrentBytes.ColumnWidth=2;
    int results = CurrentBytes.ToLEInt(CurrentBytes);
    //instead of
    int results = CurrentBytes.ToLEInt();

Затем я попытался поместить объявление вне классаблок, но затем я получил ошибку, что LEInt не был определен,

int Byte::ToLEInt(){}

Когда я попытался добавить int ToLEInt(); в блок класса, то он пожаловался, что он уже определен.

Ответы [ 2 ]

0 голосов
/ 16 октября 2019
// how can we do this?
int results = CurrentBytes.ToLittleEndianInt();
//or
int results = CurrentBytes->ToLittleEndianInt();

Вы не можете. C ++ не работает таким образом.

Вместо этого вы можете сделать следующее:

// assuming DataParser is a namespace
using namespace DataParser;
int results = ToLittleEndianInt(CurrentBytes);
// or...
int results = DataParser::ToLittleEndianInt(CurrentBytes); 

Это способ обработки C ++.

Если вы хотите большеэлегантное решение, вы могли бы унаследовать от std::vector, хотя это не рекомендуется .

Гораздо лучше было бы создать собственный класс:

class myContainer {
    private:
        std::vector data;
    public:
        //...
        int ToLittleEndianInt() {
            // Process data...
            return result;
        }
};

Тогда ваш код станет следующим:

myContainer CurrentBytez;
//...
int results = CurrentBytes.ToLittleEndianInt();

Конечно, создание собственного класса потребует некоторых исследований, особенно того, как классы отличаются в C ++ от C #, но это может стоить того. В любом случае, возможно, вы захотите изучить его, так что это не имеет большого значения.

0 голосов
/ 16 октября 2019

Ошибка unresolved external symbol "public: __thiscall Byte::Byte(void)... означает, что программе компоновщика не удалось найти определение (тело) конструктора вашего класса Byte. Чтобы это исправить, вы можете добавить пустые фигурные скобки в объявления вашего конструктора и деструктора (на этот раз без точек с запятой), как показано ниже:

class Byte
{
    public:
        Byte() {}
        ~Byte() {}
        ...

или просто удалить их объявления вообще, так как в настоящее время они все равно простаивают:

class Byte
{
    public:
        //Byte();  - can be removed entirely
        //~Byte(); - can be removed entirely
        ...

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

...