Строку знак вектора для разбора выражений


Моя цель заключается в создании линейная алгебра калькулятор, который сможет рассчитать выражений, содержащих векторы, матрицы (со временем комплексные числа/мни; кватернионы), и реальные цифры. У меня есть код для разбора лексем из заданной строки в вектор объект маркер указатели:

// Function for an operator to implement.
typedef Value*(*EvaluationFunction)(Value** args);

// The associativity of the operator.
enum Associativity : unsigned char
{
    NONE = 0, LEFT = 1, RIGHT = 2
};

// The evaluator structure that would be an
// operator and a function.
struct Evaluator
{
    EvaluationFunction evalFunc;
    unsigned char args; // The argument count expected.
};

// A structure for an operator.
struct Operator : public Evaluator
{
    String id;
    Associativity association;
    unsigned int precedence;

    // A constructor to create this struct
    // anonymously.
    Operator(String&& idp, unsigned int precedencep,
        Associativity assoc = Associativity::NONE)
    {
        id = idp;
        precedence = precedencep;
        association = assoc;
        args = 2;
    }
};

...

// The list of active operators.
static std::vector<Operator*> smOperators;

// Amount of space to reserve for vector when getting tokens.
static constexpr unsigned char RESERVE_MORE_VECTOR_SPACE{ 5 };

...

// Definitions for the token types.
// 'U' indicates unused beyond token parsing.
#define U_TOKEN_LEFT_BRACKET '['
#define U_TOKEN_RIGHT_BRACKET ']'
#define TOKEN_LEFT_PARENTHESIS '('
#define TOKEN_RIGHT_PARENTHESIS ')'
#define TOKEN_OPERATOR 'o'
#define TOKEN_FUNCTION 'f'
#define TOKEN_VALUE 'v'
#define TOKEN_COMMA ','

// A token structure used to represent
// expressional tokens.
struct Token
{
    String source;
    char type;
    union 
    {
        const Operator* op;
        const Function* func;
        Value* value;
    };
};

// Gets the specified operator.
static Operator* GetOperator(const char* src)
{
    for (auto& ptr : smOperators)
    {
        if (ptr->id == src)
        {
            return ptr;
        }
    }
    return NULL;
}

// Gets the tokens from an expression string.
static void GetTokens(const String& str, std::vector<Token*>& output)
{
    if (str.GetLength() == 0)
        return;

    output.reserve(RESERVE_MORE_VECTOR_SPACE);
    bool targetIsAlphaNumeric{ IsCharAlphaNumeric(str.At(0)) };
    bool readingMatrix{ false };
    unsigned int subStrIndex{ 0 };
    for (unsigned int i = 0; i < str.GetLength(); i++)
    {
        char cat = str.At(i);

        // Checks whether a matrix is being read in.
        if (readingMatrix)
        {
            if (cat == U_TOKEN_RIGHT_BRACKET)
            {
                output.emplace_back(CreateToken(str.Sub(subStrIndex - 1, i + 1))); // subStrIndex - 1 is there to include the left bracket
                                                                                   // and i + 1 is to include the right bracket.
                subStrIndex = i + 1;
                readingMatrix = false;
            }
            continue;
        }

        // If it's a non-unary, non-function, operator.
        if (cat == TOKEN_LEFT_PARENTHESIS
            || cat == TOKEN_RIGHT_PARENTHESIS
            || cat == TOKEN_COMMA
            || cat == U_TOKEN_LEFT_BRACKET)
        {
            // Checks whether the character is
            // the beginning of a matrix definition.
            if (cat == U_TOKEN_LEFT_BRACKET)
            {
                readingMatrix = true;
            }

            // Checks whether there should be a token
            // pushed onto the tokens vector
            if (subStrIndex != i)
            {
                output.emplace_back(CreateToken(str.Sub(subStrIndex, i)));
            }

            // Checks if a value is followed by a left parentheses.
            // Then it will insert a multiplication operator.
            if (output.size() > 0)
            {
                auto& lastElem = output.at(output.size() - 1);
                if ((cat == TOKEN_LEFT_PARENTHESIS 
                     || cat == U_TOKEN_LEFT_BRACKET
                     && (lastElem->type == TOKEN_VALUE
                         || lastElem->type == TOKEN_RIGHT_PARENTHESIS
                         || lastElem->type == U_TOKEN_RIGHT_BRACKET)))
                {
                    Token* t = new Token;
                    t->source = "*";
                    t->op = GetOperator("*");
                    t->type = TOKEN_OPERATOR;
                    output.emplace_back(t);
                }
            }

            // If the operator is not a comma or left bracket, it will
            // be pushed on the vector.
            if (cat != TOKEN_COMMA && cat != U_TOKEN_LEFT_BRACKET)
            {
                output.emplace_back(CreateToken(str.Sub(i, i + 1)));
            }

            subStrIndex = i + 1;

            // If there are still more characters to read,
            // then the target will be reassigned and the
            // substring index recalculated.
            if (i + 1 < str.GetLength())
            {
                targetIsAlphaNumeric = IsCharAlphaNumeric(str.At(i + 1));
                subStrIndex = i + 1;
            }

            // If the vector is out of capacity, it will allocate
            // 5 more slots just for efficiency.
            if (output.capacity() == output.size())
            {
                output.reserve(output.capacity() + RESERVE_MORE_VECTOR_SPACE);
            }
        }
        // If the end of the current token has been reached:
        else if((!targetIsAlphaNumeric && IsCharAlphaNumeric(cat))
                || (targetIsAlphaNumeric && !IsCharAlphaNumeric(cat)) )
        {
            // Places the token into the vector and adjusts
            // the target and substring index.
            Token* newToken = CreateToken(str.Sub(subStrIndex, i));

            // Makes sure that if the expression starts with an
            // operator (usually a negative sign), it will be, for example,
            // "0 - x" instead of "- x"; so it won't break.
            if (newToken->type == TOKEN_OPERATOR && output.size() == 0)
            {
                Token* t = new Token;
                t->source = "0";
                t->value = new Number(0);
                t->type = TOKEN_VALUE;
                output.emplace_back(t);
            }

            // Checks to see if a right parenthesis is followed by
            // a number, and if so it will insert a multiplication operator
            // between the number and the parenthesis.
            if (output.size() > 0 
                && newToken->type == TOKEN_VALUE 
                && output.at(output.size() - 1)->type == TOKEN_RIGHT_PARENTHESIS)
            {
                Token* t = new Token;
                t->source = "*";
                t->op = GetOperator("*");
                t->type = TOKEN_OPERATOR;
                output.emplace_back(t);
            }

            output.emplace_back(newToken);
            subStrIndex = i;
            targetIsAlphaNumeric = !targetIsAlphaNumeric;

            // If the vector is out of capacity, it will allocate
            // 5 more slots just for efficiency.
            if (output.capacity() == output.size())
            {
                output.reserve(output.capacity() + RESERVE_MORE_VECTOR_SPACE);
            }
        }
    }

    // Checks to see if another token can
    // be placed into the vector.
    if (subStrIndex != str.GetLength())
    {
        Token* newToken = CreateToken(str.Sub(subStrIndex, str.GetLength()));

        // Checks to see if a right parenthesis is followed by
        // a number, and if so it will insert a multiplication operator
        // between the number and the parenthesis.
        if (output.size() > 0 
            && newToken->type == TOKEN_VALUE 
            && output.at(output.size() - 1)->type == TOKEN_RIGHT_PARENTHESIS)
        {
            Token* t = new Token;
            t->source = '*';
            t->op = GetOperator("*");
            t->type = TOKEN_OPERATOR;
            output.emplace_back(t);
        }

        output.emplace_back(newToken);
    }

    // Resizes the vector to fit the contents
    // (may not be necessary, but for now it will stay).
    output.shrink_to_fit();
}

Строку.ч:

#ifndef _STRING_H
#define _STRING_H

#include <vector>
#include <iostream>

// Enumeration that will define how
// a string will be constructed.
enum StringMode : char 
{
    DEFAULT = 0, REPLACE_WHITE_SPACE = 1
};

// Sets the mode for which the
// Strings will be input through istream
// by.
void SetStringInputMode(StringMode mode);

// Gets the mode for which the
// Strings will be input through istream
// by.
StringMode GetStringInputMode();

// This class should containg a pointer
// to a character/character array, but should
// use less memory than the std::string. The main
// purpose for this class is to simplify the
// "routing" of char*s when evaluating expressions.
class String final 
{
    // Uses the std::istream to input a string
    // object.
    friend std::istream& operator>>(std::istream&, String&);

    // Uses the std::ostream to output the specified string.
    friend std::ostream& operator<<(std::ostream&, const String&);
private:
    char* c_str; // The "c string"
    unsigned int length; // The length of the "c string"

    // Compares the characters between the two strings starting
    // at the specified index in this string.
    void inline CompareFrom(unsigned int, const String&, bool&) const;
public:
    // A default constructor just in case
    // we need the string to be input from
    // an istream.
    String();

    // Defining a copy constructor to make
    // sure the pointers are transfered correctly
    // and the string is generated with a specified mode.
    String(const String&, StringMode = StringMode::DEFAULT);

    // The constructor for the String class
    // that takes the original C string, and it
    // will copy it into a new heap allocated space
    // with a specified mode.
    String(const char*, StringMode = StringMode::DEFAULT);

    // Creates a new string that takes in a
    // buffer size but sets all of the values
    // to 0 / null.
    String(unsigned int size);

    // A destructor is defined for this class
    // to destroy the allocated heap memory.
    ~String();

    // Deletes the current c_str and
    // redefines this string with the specified
    // length.
    void Redefine(unsigned int);

    // Resizes this string to match the
    // specified length.
    void Resize(unsigned int);

    // Gets the pointer to the heap allocated
    // char*
    const char* GetPointer() const;

    // Gets a substring. First index is inclusive;
    // second index is exclusive.
    String Sub(unsigned int, unsigned int) const;

    // Gets the length of this string
    unsigned int GetLength() const;

    // Places the specified character c into
    // the index position 'index'
    void Put(char, unsigned int);

    // Gets the character at the specified
    // index.
    char At(unsigned int) const;

    // Finds the first instance of the specified
    // character in this string.
    unsigned int Find(char) const;

    // Attempts to find an instance of a specified
    // string within this string.
    unsigned int Find(const String&) const;

    // Attempts to find all matches of the specified
    // string within this string.
    void FindAll(const String&, std::vector<unsigned int>&) const;

    // An assignment operator to make sure
    // the operator from one string gets
    // transfered correctly to this string.
    void operator=(const String&);

    // A comparison operator to check whether
    // two strings are identical.
    // True = identical; False = not identical.
    bool operator==(const String&) const;

    // A comparison operator to check whether
    // two strings are not identical.
    // True = not identical; False = identical.
    bool operator!=(const String&) const;

    // A comparison operator to check whether
    // two strings are identical.
    // True = identical; False = not identical.
    bool operator==(const char*) const;

    // A comparison operator to check whether
    // two strings are not identical.
    // True = not identical; False = identical.
    bool operator!=(const char*) const;

    // A comparison operator to check whether
    // this string and specified char are identical.
    // True = identical; False = not identical.
    bool operator==(const char) const;

    // A comparison operator to check whether
    // the string and char are not identical.
    // True = not identical; False = identical.
    bool operator!=(const char) const;
};

// This class will be used when an
// unknown number of characters need to
// be put into a string.
class StringBuffer final
{
private:
    char** buffers; // The collection of buffers.
    unsigned int unitBufferSize; // The size of one individual buffer.
    unsigned int capacity; // The capacity of the buffers array.
    unsigned int currentBuffer; // Keeps track of an open buffer location.
    unsigned int iterator; // Iterator that will keep track of the current buffer location.

    // Places a character into the buffer using
    // and inline method.
    inline void PutChar(char c);

    // Adds n amount of buffers.
    void AddBuffers(unsigned int);

    // Locates a character at a specified
    // location.
    inline char At(unsigned int) const;
public:
    // A default constructor that will be used to
    // initialize the currentBuffer pointer to a
    // heap allocated one.
    explicit StringBuffer(unsigned int = 0, unsigned int = 64);

    // No need to have a copy constructor.
    StringBuffer(StringBuffer&) = delete;

    // A destructor to remove the heap allocated
    // buffers array.
    ~StringBuffer();

    // Gets the size of a unit buffer.
    unsigned int GetUnitBufferSize() const;

    // Sets the size of a unit buffer.
    void SetUnitBufferSize(unsigned int);

    // Gets the count of unit buffers.
    unsigned int GetUnitBufferCount() const;

    // Gets the length of the buffer.
    unsigned int GetLength() const;

    // Input a character into the buffer.
    void operator<<(char);

    // Inputs a string into the buffer.
    void operator<<(const String&);

    // Inputs a string into the buffer.
    void operator<<(const char*);

    // Copies the buffer data into a string object.
    void CopyInto(String&) const;

    // Converts the buffers into a String object.
    String Collect() const;

    // Clears any data stored in the buffers.
    // The size remains the same.
    void Reset(unsigned int = 0);

    // No need for an assignment operator.
    void operator=(StringBuffer&) = delete;

    // Checks whether a string matches what is in
    // this buffer.
    bool operator==(const String&) const;
};

#endif // _STRING_H

String.cpp:

#include <stdlib.h>
#include <string.h>
#include <stdexcept>
#include "String.h"

#define PRIVATE

// ----- NON-MEMBER FUNCTIONS -----

static StringMode INPUT_MODE{ StringMode::DEFAULT }; // Mode by which strings are input by.

// Sets the mode for which the
// Strings will be input through istream
// by.
void SetStringInputMode(StringMode mode)
{
    INPUT_MODE = mode;
}

// Gets the mode for which the
// Strings will be input through istream
// by.
StringMode GetStringInputMode()
{
    return INPUT_MODE;
}

// ------ STRING -------

// Uses the std::istream to input a string
// object.
std::istream& operator>>(std::istream& input, String& output)
{
    StringBuffer buffer{ 1 };
    char n;
    while (input.get(n))
    {
        if (n == 10)
            break;

        if (n == ' ' && INPUT_MODE == StringMode::REPLACE_WHITE_SPACE)
            continue;

        buffer << n;
    }
    buffer.CopyInto(output);
    return input;
}

// Uses the std::ostream to output the specified string.
std::ostream& operator<<(std::ostream& output, const String& str)
{
    output << str.c_str;
    return output;
}

// A default constructor just in case
// we need the string to be input from
// an istream.
String::String()
    : c_str{ (char*)malloc(sizeof(char)) }, length{ 0 }
{
    *(c_str) = NULL;
}

// Defining a copy constructor to make
// sure the pointers are transfered correctly
// and the string is generated with a specified mode.
String::String(const String& str, StringMode mode)
    : length{ 0 }
{
    if (mode == StringMode::REPLACE_WHITE_SPACE)
    {
        for (unsigned int i = 0; i < str.length; i++)
            if (*(str.c_str + i) != ' ')
                length++;

        if (length == 0)
        {
            c_str = nullptr;
            return;
        }

        c_str = (char*)malloc(length * sizeof(char) + 1);
        unsigned int whiteSpaceCount{ 0 };
        for (unsigned int i = 0; i < str.length; i++)
        {
            if (*(str.c_str + i) != ' ')
            {
                whiteSpaceCount++;
                continue;
            }

            *(c_str + i - whiteSpaceCount) = *(str.c_str + i);
        }
        *(c_str + length) = 0;
    }
    else
    {
        length = str.length;
        c_str = (char*)malloc(length * sizeof(char) + 1);
        strcpy(c_str, str.c_str);
    }
}

// The constructor for the String class
// that takes the original C string, and it
// will copy it into a new heap allocated space
// with a specified mode.
String::String(const char* defined, StringMode mode)
    : length{ 0 }
{
    if (mode == StringMode::REPLACE_WHITE_SPACE)
    {
        for (unsigned int i = 0; defined[i] != 0; i++)
        {
            if (*(defined + i) != ' ')
                length++;
        }

        if (length == 0)
        {
            c_str = nullptr;
            return;
        }

        c_str = (char*)malloc(length * sizeof(char) + 1);
        unsigned int whiteSpaceCount{ 0 };
        for (unsigned int i = 0; defined[i] != 0; i++)
        {
            if (*(defined + i) == ' ')
            {
                whiteSpaceCount++;
                continue;
            }

            *(c_str + i - whiteSpaceCount) = *(defined + i);
        }
        *(c_str + length) = 0;
    }
    else 
    {
        length = strlen(defined);
        c_str = (char*)malloc(length * sizeof(char) + 1);
        strcpy(c_str, defined);
    }
}

// Creates a new string that takes in a
// buffer size but sets all of the values
// to 0 / null.
String::String(unsigned int size)
    : length{ size } 
{
    if (size < 0)
    {
        c_str = nullptr;
        length = 0;
        return;
    }

    c_str = (char*)malloc(length + 1);
    for (unsigned int i = 0; i < length; i++)
        *(c_str + i) = 0;
    *(c_str + length) = 0;
}

// A destructor is defined for this class
// to destroy the allocated heap memory.
String::~String() 
{
    delete c_str;
}

// Compares the characters between the two strings starting
// at the specified index in this string.
PRIVATE void inline String::CompareFrom(unsigned int si, const String& ss, bool& so) const
{
    so = true;
    for (unsigned int i = 0; i < ss.GetLength(); i++)
    {
        if (*(c_str + i + si) != *(ss.c_str + i))
        {
            so = false;
            break;
        }
    }
}

// Deletes the current c_str and
// redefines this string with the specified
// length.
void String::Redefine(unsigned int nSize)
{
    free(c_str);
    length = nSize;
    c_str = (char*)malloc(length * sizeof(char) + 1);
    *(c_str + nSize) = 0;
}

// Resizes this string to match the
// specified length.
void String::Resize(unsigned int nSize)
{
    if (nSize < 0)
    {
        return;
    }

    char* new_str = (char*)malloc(nSize * sizeof(char) + 1);
    for (unsigned int i = 0; i < nSize; i++)
    {
        if (i < length)
            *(new_str + i) = *(c_str + i);
        else
            break;
    }
    *(new_str + nSize) = 0;
    free(c_str);
    c_str = new_str;
    length = nSize;
}

// Gets the pointer to the heap allocated
// char*
const char* String::GetPointer() const 
{
    return c_str;
}

// Gets a substring.
String String::Sub(unsigned int begin, unsigned int end) const
{
    if (begin < 0 || begin >= length || end < 0 || end > length || begin >= end)
    {
        return String{ static_cast<unsigned int>(0) };
    }

    String sub{ end - begin };
    for (unsigned int i = 0; i < sub.length; i++)
    {
        *(sub.c_str + i) = *(c_str + begin + i);
    }
    return sub;
}

// Gets the length of this string
unsigned int String::GetLength() const 
{
    return length;
}

// Places the specified character c into
// the index position 'index'
void String::Put(char c, unsigned int index)
{
    if (index < 0 || index >= length)
        return;

    *(c_str + index) = c;
}

// Gets the character at the specified
// index.
char String::At(unsigned int index) const
{
    if (index < 0 || index >= length)
        return NULL;

    return *(c_str + index);
}

// Finds the first instance of the specified
// character in this string.
unsigned int String::Find(char c) const
{
    for (unsigned int i = 0; i < length; i++)
        if (*(c_str + i) == c)
            return i;

    return length;
}

// Attempts to find an instance of a specified
// string within this string.
unsigned int String::Find(const String& str) const
{
    if (str.GetLength() > length || str.GetLength() == 0 || length == 0)
    {
        return length;
    }

    bool output;
    for (unsigned int i = 0; i <= length - str.GetLength(); i++)
    {
        CompareFrom(i, str, output);
        if (output)
        {
            return i;
        }
    }
    return length;
}

// Attempts to find all matches of the specified
// string within this string.
void String::FindAll(const String& str, std::vector<unsigned int>& ind) const
{
    unsigned int strLen = str.GetLength();
    if (strLen > length || strLen == 0 || length == 0)
    {
        return;
    }
    for (unsigned int i = 0; i < length - strLen; i++)
    {
        bool found{ false };
        CompareFrom(i, str, found);
        if (found)
        {
            ind.emplace_back(i);
            i += strLen - 1;
        }
    }
}

// An assignment operator to make sure
// the operator from one string gets
// transfered correctly to this string.
void String::operator=(const String& str) 
{
    free(c_str);
    c_str = (char*)malloc(str.length * sizeof(char) + 1);
    strcpy(c_str, str.c_str);
    length = str.length;
}

// A comparison operator to check whether
// two strings are identical.
// True = identical; False = not identical.
bool String::operator==(const String& str) const
{
    if (str.length != length)
        return false;

    for (unsigned int i = 0; i < length; i++)
        if (*(c_str + i) != *(str.c_str + i))
            return false;

    return true;
}

// A comparison operator to check whether
// two strings are not identical.
// True = not identical; False = identical.
bool String::operator!=(const String& str) const
{
    if (str.length != length)
        return true;

    for (unsigned int i = 0; i < length; i++)
        if (*(c_str + i) != *(str.c_str + i))
            return true;

    return false;
}

// A comparison operator to check whether
// two strings are identical.
// True = identical; False = not identical.
bool String::operator==(const char* str) const
{
    unsigned int len = strlen(str);
    if (length != len)
        return false;

    for (unsigned int i = 0; i < len; i++)
        if (*(c_str + i) != *(str + i))
            return false;

    return true;
}

// A comparison operator to check whether
// two strings are not identical.
// True = not identical; False = identical.
bool String::operator!=(const char* str) const
{
    unsigned int len = strlen(str);
    if (length != len)
        return true;

    for (unsigned int i = 0; i < len; i++)
        if (*(c_str + i) != *(str + i))
            return true;

    return false;
}

// A comparison operator to check whether
// this string and specified char are identical.
// True = identical; False = not identical.
bool String::operator==(const char c) const
{
    if (length != 1)
        return false;

    return *c_str == c;
}

// A comparison operator to check whether
// the string and char are not identical.
// True = not identical; False = identical.
bool String::operator!=(const char c) const
{
    if (length != 1)
        return true;

    return *c_str != c;
}

// ------ STRING BUFFER -------

// A default constructor that will be used to
// initialize the currentBuffer pointer to a
// heap allocated one.
StringBuffer::StringBuffer(unsigned int cap, unsigned int unitBufSize)
    : unitBufferSize{ unitBufSize < 1 ? 1 : unitBufSize }, iterator { 0 }, currentBuffer{ 0 }, capacity{ cap < 1 ? 1 : cap }
{
    buffers = (char**)malloc(sizeof(char*) * capacity);

    for (unsigned int i = 0; i < capacity; i++)
    {
        *(buffers + i) = (char*)malloc(sizeof(char) * unitBufferSize);
    }
}

// A destructor to remove the heap allocated
// buffers array.
StringBuffer::~StringBuffer()
{
    for (unsigned int i = 0; i < capacity; i++)
    {
        free(*(buffers + i));
        *(buffers + i) = nullptr;
    }
    free(buffers);
}

// Gets the size of a unit buffer.
unsigned int StringBuffer::GetUnitBufferSize() const
{
    return unitBufferSize;
}

// Sets the size of a unit buffer.
void StringBuffer::SetUnitBufferSize(unsigned int nBuffSize)
{
    if (currentBuffer == 0 && iterator == 0) // Nothing has been appended so far.
        unitBufferSize = nBuffSize == 0 ? 1 : nBuffSize;
}

// Gets the count of unit buffers.
unsigned int StringBuffer::GetUnitBufferCount() const
{
    return capacity + 1;
}

// Gets the length of the buffer.
unsigned int StringBuffer::GetLength() const
{
    return (unitBufferSize * currentBuffer) + iterator;
}

// Places a character into the buffer using
// and inline method.
PRIVATE inline void StringBuffer::PutChar(char c)
{
    if (iterator == unitBufferSize)
    {
        if (currentBuffer + 1 == capacity)
            AddBuffers(1);

        currentBuffer++;
        iterator = 0;
    }
    *(*(buffers + currentBuffer) + iterator++) = c; // place char into next available spot
}

// Resizes the buffers array to a specified amount.
PRIVATE void StringBuffer::AddBuffers(unsigned int amt)
{
    char** nAlloc = (char**)malloc(sizeof(char*) * (capacity + amt));
    for (unsigned int i = 0; i < amt + capacity; i++)
    {
        if (i < capacity && buffers != nullptr)
            *(nAlloc + i) = *(buffers + i);
        else
            *(nAlloc + i) = (char*)malloc(sizeof(char) * unitBufferSize);
    }

    capacity += amt;
    free(buffers);
    buffers = nAlloc;
}

// Locates a character at a specified
// location.
PRIVATE inline char StringBuffer::At(unsigned int loc) const
{
    return (*(*(buffers + (loc / unitBufferSize)) + (loc % unitBufferSize)));
}

// Input a character into the buffer.
void StringBuffer::operator<<(char n)
{
    PutChar(n);
}

// Inputs a string into the buffer.
void StringBuffer::operator<<(const String& str)
{
    for (unsigned int i = 0; i < str.GetLength(); i++)
    {
        PutChar(str.At(i));
    }
}

// Inputs a string into the buffer.
void StringBuffer::operator<<(const char* str)
{
    if (str == nullptr)
        return;

    unsigned int i{ 0 };
    while (*(str + i) != 0)
    {
        PutChar(*(str + i++));
    }
}

// Copies the buffer data into a string object.
void StringBuffer::CopyInto(String& str) const
{
    if (currentBuffer == 0 && iterator == 0)
        return;

    unsigned int nStrLen = (currentBuffer * unitBufferSize) + iterator;
    str.Redefine(nStrLen);
    for (unsigned int i = 0; i < nStrLen; i++)
    {
        str.Put(At(i), i);
    }
}

// Converts the buffers into a String object.
String StringBuffer::Collect() const
{
    if (currentBuffer == 0 && iterator == 0)
        return String{};

    unsigned int nStrLen = (currentBuffer * unitBufferSize) + iterator;
    String output{ nStrLen };
    for (unsigned int i = 0; i < nStrLen; i++)
    {
        output.Put(At(i), i);
    }
    return output;
}

// Clears any data stored in the buffers.
// The size remains the same.
void StringBuffer::Reset(unsigned int cap)
{
    for (unsigned int i = 0; i < capacity; i++)
    {
        free(*(buffers + i));
    }
    capacity = cap < 1 ? 1 : cap;
    free(buffers);
    buffers = (char**)malloc(sizeof(char*) * capacity);

    for (unsigned int i = 0; i < capacity; i++)
    {
        *(buffers + i) = (char*)malloc(sizeof(char) * unitBufferSize);
    }

    currentBuffer = 0;
    iterator = 0;
}

// Checks whether a string matches what is in
// this buffer.
bool StringBuffer::operator==(const String& str) const
{
    if (str.GetLength() != iterator + (currentBuffer * unitBufferSize))
    {
        return false;
    }

    for (unsigned int i = 0; i < str.GetLength(); i++)
    {
        if (str.At(i) != At(i))
            return false;
    }
    return true;
}

Редактировать: после возвращения через класс String, я понятия не имею, что я делаю с "маршрутизация указателей типа char", но я также хотел ввести строку непосредственно из std::cin и иметь возможность заменить бело-пространстве в качестве входного идет. Было бы лучше использовать std::string?

Хотя калькулятор в целом в настоящее время оценивается в среднем выражения, такие как 5+[(2+2)*5;5] очень быстро (1/10 миллисекунды), с моей внешностью GetTokens функция может использовать некоторую работу. Я особенно заботился о повышении уровня второй if блок на главной for петли, как я чувствую, сравнивая характер снова в блоке является избыточным. Я пытался как-то вписаться в switch заявление, но это бы никогда не сработало так, как я хочу.

Это довольно много его. Так как я относительно новый для C++ (и "низкий уровень" программирования в целом), пожалуйста, прокомментировать какие-либо улучшения в коде в целом, и если что-то можно сделать лучше/эффективнее.



209
6
задан 11 марта 2018 в 04:03 Источник Поделиться
Комментарии
1 ответ

В целом я думаю, что это достаточно разумно. Я думаю, что самыми важными вопросами являются склонность изобретать велосипед, а нужно ломать вещи на более мелкие части, чтобы сделать его более читабельным. Вот некоторые предложения.

Макросы

Используя #define для констант с-изма. В C++ вы должны предпочитают использовать либо enumS или const значения. Таким образом, для определения различных маркеров, я бы, наверное, сделать что-то вроде этого:

enum TokenType : char {
U_TOKEN_LEFT_BRACKET = '[',
U_TOKEN_RIGHT_BRACKET = ']',
// ... etc.
};

Или, если вы предпочитаете использовать constтогда что-то вроде этого:

static const char U_TOKEN_LEFT_BRACKET = `[`;
static const char U_TOKEN_RIGHT_BRACKET = ']';
// ... etc.

Изобретать колесо

Вы, кажется, писали много кода, который уже существует в другом месте. Например, String класс, IsCharAlphaNumeric() функции, etc. Вы должны искать вещи онлайн, прежде чем внедрять что-то свое. Некоторые особенности:

Вы говорите:


Я также хотел ввести строку напрямую от std::Cin и сможет заменить бело-пространстве в качестве входного идет. Было бы лучше использовать СТД::стринг?

Почти наверняка, но попробовать его и посмотреть. std::string используется в тысячах реальные программы работают миллионы пользователей. Это хорошо проверенный и эффективный. Но единственный способ узнать наверняка, будет ли это лучше, чем то, что вы в настоящее время используете, чтобы попробовать его, а затем профиль код, чтобы увидеть, если это быстрее.

Другое дело, что ваш String класс имеет некоторые проблемы. Конструктор не проверить возвращаемое значение malloc()так вполне возможно, что нет памяти, выделенной под строку, но она будет написана в любом случае. Это также делает больше работы, чем это необходимо. Например в конструктор копирования, в else случае, вы можете просто позвонить strdup() вместо malloc() затем strcpy().

В IsCharAlphaNumeric() функцию можно заменить isalnum() в <ctype.h>. В GetOperator() функция может быть заменен вызовом std::find_if().

Упростить

Я вижу ряд вещей, которые могут быть упрощены, а также. В Token класс не имеет конструкторов. Вы могли бы просто сделать 3 из них - по одному для каждой части в union. Что бы сделать 4 места, где можно вручную выделить один (без проверки, если это удалось), а затем вручную заполнить его в одну линию.

Также, почему output а vector из указателей на Tokenы? Если бы это были просто vector из Token объекты, которые вы могли бы использовать emplace_back() добавить их без дополнительного вызова CreateToken(). (Кстати, я не вижу код для этой функции. Учитывая название это выглядит ужасно много, как конструкторы я предложил в предыдущем пункте.) В любом случае, если вы имеете дело с меньшим количеством указателей, у вас меньше шансов забыть, чтобы проверить, если выделить их удалось.

Это:

if((!targetIsAlphaNumeric && IsCharAlphaNumeric(cat))
|| (targetIsAlphaNumeric && !IsCharAlphaNumeric(cat)) )

можно упростить эту:

if (targetIsAlphaNumeric != IsCharAlphaNumeric(cat))

Вы проверили, что ваши попытки оставить больше пространства на самом деле эффективнее? std::vector довольно эффективным уже. (Также, Вы имеете в виду эффективное в пространстве или во времени?)

Вы можете сделать петли легче читать и понимать, разбив ее на функции, такой:

for (unsigned int i = 0; i < str.GetLength(); i++)
{
if (readingMatrix)
{
ParseMatrixCharacter(cat, i, output);
}
else if (isRegularOperator(cat))
{
ParseRegularOperator(cat, i, output);
}
else if (targetIsAlphaNumeric != IsCharAlphaNumeric(cat))
{
ParseEnd(cat, i, output);
}
}

Именования

Большинство ваших имен понятны. Там было всего несколько я бы предложил работать на. Что cat имеешь в виду? "характер у"? Если это так, это так назвать. Почему бы не назвать t token? Это всего лишь несколько символов и более читаемым.

1
ответ дан 11 марта 2018 в 06:03 Источник Поделиться