Error of indefinite parameter function in C++ 17

0

I wrote a string class and I want to write a function like Format() function in MFC's CString class.But when I use it,it always cause some "random" errors and doesn't work at all.

I use sprintf() to make this function,but when I put the argument that I got from va_arg() ,it told me that argument is invalid. By use std::string().c_str() ,this error has gone,but I got this error:

"At 0x00B326A0(Inside UString.exe)Csusing error: 0xC0000005: Access conflict occurs at read location 0x77000073" or it will turn to my throw() line.

Here are all the "format" function and its support functions code:

    std::string UString::SUBSTR(std::string src, int start, int stop)
{
    if (start > stop)
    {
        return "null";
    }
    if ((stop - start) > src.length())
    {
        return "overload";
    }
    char* tmp = new char[src.length()];
    for (int i = start; i <= stop; i++)
    {
        tmp[i - start] = src[i];
    }
    std::string ret = std::string(tmp);
    delete[] tmp;
    return ret;
}
std::wstring UString::STW(const std::string & str)
{
    std::wstring ret;
    try {
        std::wstring_convert< std::codecvt_utf8<wchar_t> > wcv;
        ret = wcv.from_bytes(str);
    }
    catch (const std::exception & e) {
        std::cerr << e.what() << std::endl;
    }
    return ret;
}
bool UString::Matchopt(std::string s)
{
    const std::string opt[10] = { "d","o","x","X","c","s","f","ld","lld","lf" };
    for (int i = 0; i < 10; i++)
    {
        if (s == opt[i])
            return true;
    }
    return false;
}
bool UString::IsLegalFormatStringEnd(char c)
{
    std::string sg = "doxXsfc";
    for (int i = 0; i < sg.length(); i++)
    {
        if (c == sg[i])
            return true;
    }
    return false;
}
bool UString::IsLegalFormatString(std::string fms)
{
    UString ustr = fms;
    return ustr.legalstring(std::string("%-0123456789.cdoflsxX"));
}
std::string UString::GetSubFormatString(std::string full,int pos)
{
    for (int i = pos; i < full.length(); i++)
    {
        if (IsLegalFormatStringEnd(full[i]))
        {
            printf("format string:%s,result:%d\n", SUBSTR(full, pos, i).c_str(), IsLegalFormatString(SUBSTR(full, pos, i)));
            if (IsLegalFormatString(SUBSTR(full, pos, i)))
            {
                return SUBSTR(full, pos, i);
            }
            else
            {
                throw("UString: Unlegal Format String");
                return "";
            }
        }
    }
    throw("UString: Unlegal Format String");
    return "";
}
std::string UString::GetCoreString(std::string sfs)
{
    UString utmp = sfs;
    if (utmp.length() == 0)
    {
        throw("UString: Cannot get sub string from a empty source string");
    }
    else if (utmp.length() == 1)
    {
        if (Matchopt(utmp.std_str()))
        {
            return utmp.std_str();
        }
        else
        {
            throw("UString: Unlegal Format String");
        }
    }
    else if (utmp.length() == 2)
    {
        if (Matchopt(utmp.substr(1, 1)))
        {
            return utmp.substr(1, 1);
        }
        else if (Matchopt(utmp.substr(0, 1)))
        {
            return utmp.substr(0, 1);
        }
        else
        {
            throw("UString: Unlegal Format String");
        }
    }
    else if (Matchopt(utmp.substr(utmp.length() - 1, utmp.length() - 1)))
    {
        return utmp.substr(utmp.length() - 1, utmp.length() - 1);
    }
    else if (Matchopt(utmp.substr(utmp.length() - 2, utmp.length() - 1)))
    {
        return utmp.substr(utmp.length() - 2, utmp.length() - 1);
    }
    else if (Matchopt(utmp.substr(utmp.length() - 3, utmp.length() - 1)))
    {
        return utmp.substr(utmp.length() - 3, utmp.length() - 1);
    }
    else
    {
        throw("UString: Unlegal Format String");
        return "";
    }
}
void UString::format(std::string cmd, ...)
{
    va_list vl;
    int cnt = 0;
    for (int i = 0; i < cmd.length(); i++)
    {
        if (i != (cmd.length() - 1))
        {
            if ((cmd[i] == '%') && (cmd[i + 1] != '%'))
            {
                cnt++;
            }
        }
    }
    if (cnt == 0)
    {
        str = std::string(cmd);
        return;
    }
    else
    {
        va_start(vl, cnt);
        std::string* cmdgroup = new std::string[cnt + 1];
        int* pos = new int[cnt];
        char* tmp = nullptr;
        int posi = 0, posold = 0;
        for (int i = 0; i < cmd.length(); i++)
        {
            if (i != (cmd.length() - 1))
            {
                if ((cmd[i] == '%') && (cmd[i + 1] != '%'))
                {
                    pos[posi] = i;
                    posi++;
                }
            }
        }
        for (int i = 0; i < cnt; i++)
        {
            std::string full = GetSubFormatString(cmd, pos[i]);
            std::string opt = GetCoreString(full);
            char* buffer = NULL;
            if (opt == "d" || opt == "o" || opt == "x" || opt == "X")
            {
                int tmpi = va_arg(vl, int);
                sprintf(buffer, full.c_str(), tmpi);
            }
            else if (opt == "c")
            {
                char tmpc = va_arg(vl, char);
                sprintf(buffer, full.c_str(), tmpc);
            }
            else if (opt == "s")
            {
                const char* tmpcp = va_arg(vl, char*);
                std::string tmps = std::string(tmpcp);
                sprintf(buffer, full.c_str(), tmps.c_str());
            }
            else if (opt == "f")
            {
                float tmpf = va_arg(vl, float);
                sprintf(buffer, full.c_str(), tmpf);
            }
            else if (opt == "ld")
            {
                long tmpl = va_arg(vl, long);
                sprintf(buffer, full.c_str(), tmpl);
            }
            else if (opt == "lld")
            {
                long long tmpll = va_arg(vl, long long);
                sprintf(buffer, full.c_str(), tmpll);
            }
            else if (opt == "lf")
            {
                double tmpd = va_arg(vl, double);
                sprintf(buffer, full.c_str(), tmpd);
            }
            delete[] buffer;
            cmdgroup[i + 1] = std::string(buffer);
        }
        if (cmd[0] != '%')
        {
            cmdgroup[0] = SUBSTR(cmd, 0, pos[0] - 1);
        }
        else 
            cmdgroup[0] = "";
        va_end(vl);
        str = "";
        for (int i = 0; i < cnt + 1; i++)
        {
            str = str + cmdgroup[i];
        }
        delete[] pos;
        delete[] tmp;
        delete[] cmdgroup;
    }
    return;
}
bool UString::legalstring(std::string sstr)
{
    return legalstring(STW(sstr));
}
bool UString::legalstring(std::wstring wstr)
{
    std::wstring ws = STW(str);
    for (int i = 0; i < length(); i++)
    {
        for (int j = 0; j < wstr.length(); j++)
        {
            if (ws[i] == wstr[j])
                break;
            else if (j == (wstr.length() - 1))
                return false;
        }
    }
    return true;
}

It should work like this:

UString ustr; ustr.format("test:%d",5); std::cout<<ustr<<std::endl;

The output should be: test:5

c++
visual-studio
visual-studio-2017
printf
asked on Stack Overflow Aug 30, 2019 by Object Unknown • edited Aug 30, 2019 by Nicol Bolas

1 Answer

1

There is a far quicker way to implement your Format() member function, using the mystical vsnprintf() function! Try this:

#include <stdio.h>
#include <stdarg.h>
#include <iostream>

class UString {
public:
    char buffer[4096]; // Just to keep the code simple!
    UString() : buffer {""} {}
    inline void Format(const char* fmt, ...) {
        va_list argList; va_start(argList, fmt);
        vsnprintf(buffer, 4095, fmt, argList);
        va_end(argList);
    }
    operator char* () { return buffer; }
};

int main()
{
    UString test;
    printf("Initial: >>>%s<<<\n", test.operator char *());
    int i = 12; double d = 3.1415926536; const char* s = "Hello, World!";
    test.Format("%s i = %d, d = %lf", s, i, d);
    printf("Tested: >>>%s<<<\n", test.operator char *());
    // Your test ...
    UString ustr;
    ustr.Format("test:%d", 5);
    std::cout << ustr << std::endl;
    return 1;
}

EDIT: Corrected to more 'standard' vsnprintf().

answered on Stack Overflow Sep 6, 2019 by Adrian Mole • edited Sep 6, 2019 by Adrian Mole

User contributions licensed under CC BY-SA 3.0