Skip to main content
Tweeted twitter.com/StackCodeReview/status/1454417808037658624
deleted 26 characters in body
Source Link
jdt
  • 2.5k
  • 6
  • 22
#include <charconv>
#include <iostream>
#include <limits>
#include <stdexcept>
#include <sstream>
#include <string>
#include <string_view>
#include <vector>

namespace jdt
{
    struct Segment
    {
        Segment(std::size_t offset, std::size_t count, std::size_t argument = std::numeric_limits<std::size_t>::max(), bool braceError = false)
            : offset{ offset },
            count{ count },
            argument{ argument },
            braceError{ braceError }
        {
        }

        std::size_t offset;
        std::size_t count;
        std::size_t argument;
        bool braceError;
    };

    struct Argument
    {
        Argument(const std::string& format)
            : format{ format },
            handled{ false }
        {
        }

        std::string format;
        std::string text;
        bool handled;
    };

    template<typename T>
    void format_helper(std::vector<Argument>& arguments, std::size_t& index, const T& value)
    {
        if (index < arguments.size())
        {
            Argument& arg = arguments[index];
            std::ostringstream oss;
            oss << value;
            arg.text = oss.str();
            arg.handled = true;
            index++;
        }
    }

    template<typename... Targs>
    std::string format(std::string_view str, Targs...args)
    {
        // PHASE 1 - parse format string
        std::vector<Segment> segments;
        std::vector<Argument> arguments;
        std::size_t offset = 0;
        std::size_t index = str.find('{');
        while (index != std::string::npos)
        {
            // check for literal open-braces character
            if (str[index + 1] == '{')
            {
                segments.emplace_back(offset, index - offset + 1);
                offset = index + 2;
            }
            else
            {
                // check if the next part is an argument index
                std::size_t argindex = arguments.size();
                std::size_t startOfArg = index;
                auto ret = std::from_chars(str.data() + index + 1, str.data() + str.size(), argindex);
                index = ret.ptr - str.data();
                // find closing brace
                bool braceError = false;
                std::size_t closeBrace = str.find('}', index);
                if (closeBrace == std::string::npos)
                {
                    braceError = true;
                    closeBrace = str.size();
                }
                // check for format specifier
                if (str[index] == ':')
                    index++;
                // add to vectors
                arguments.emplace_back(std::string(str.substr(index, closeBrace - index)));
                segments.emplace_back(offset, startOfArg - offset, argindex, braceError);
                offset = closeBrace + 1;
            }
            index = str.find('{', offset);
        }

        // PHASE 2 - Recursive variadic template
        index = 0;
        (format_helper(arguments, index, args), ...);

        // PHASE 3 - Stich the string back together
        std::ostringstream oss;
        for (auto& segment : segments)
        {
            oss << str.substr(segment.offset, segment.count);
            if (segment.braceError == true)
            {
                oss << "{ error: no matching '}' found";
            }
            else
            {
                if (segment.argument != std::numeric_limits<std::size_t>::max())
                {
                    if (segment.argument < arguments.size() && arguments[segment.argument].handled)
                        oss << arguments[segment.argument].text;
                    else
                        oss << format("{{ error: invalid arg index: {} }", segment.argument);
                }
            }
        }
        if (offset < str.size())
            oss << str.substr(offset);
        return oss.str();
    }
}


int main()
{
    int a = 5;
    double b = 3.14;
    std::string c("hello");

    // simple arguments
    std::cout << jdt::format("a = {}, b = {}, c = {}\n", a, b, c);

    // arguments with indexes
    std::cout << jdt::format("a = {2}, b = {1}, c = {0}\n", a, b, c);

    // literal open-braces character
    std::cout << jdt::format("let object = {{\"a\": {0}, \"b\": {1}, \"c\": \"{2}\"};\n", a, b, c);

    // too few arguments
    std::cout << jdt::format("a = {0}, b = {1}, c = {2}\n", a, b);

    // invalid argument indexes
    std::cout << jdt::format("a = {0}, b = {10}, c = {2}\n", a, b, c);

    // too many arguments
    std::cout << jdt::format("a = {2}, b = {1}, c = {0}\n", a, b, c, 3, 4, 5);

    // missing closing brace }
    std::cout << jdt::format("a = {2}, b = {1}, c = {0\n", a, b, c);
}
#include <charconv>
#include <iostream>
#include <limits>
#include <stdexcept>
#include <sstream>
#include <string>
#include <string_view>
#include <vector>

namespace jdt
{
    struct Segment
    {
        Segment(std::size_t offset, std::size_t count, std::size_t argument = std::numeric_limits<std::size_t>::max(), bool braceError = false)
            : offset{ offset },
            count{ count },
            argument{ argument },
            braceError{ braceError }
        {
        }

        std::size_t offset;
        std::size_t count;
        std::size_t argument;
        bool braceError;
    };

    struct Argument
    {
        Argument(const std::string& format)
            : format{ format },
            handled{ false }
        {
        }

        std::string format;
        std::string text;
        bool handled;
    };

    template<typename T>
    void format_helper(std::vector<Argument>& arguments, std::size_t& index, const T& value)
    {
        if (index < arguments.size())
        {
            Argument& arg = arguments[index];
            std::ostringstream oss;
            oss << value;
            arg.text = oss.str();
            arg.handled = true;
            index++;
        }
    }

    template<typename... Targs>
    std::string format(std::string_view str, Targs...args)
    {
        // PHASE 1 - parse format string
        std::vector<Segment> segments;
        std::vector<Argument> arguments;
        std::size_t offset = 0;
        std::size_t index = str.find('{');
        while (index != std::string::npos)
        {
            // check for literal open-braces character
            if (str[index + 1] == '{')
            {
                segments.emplace_back(offset, index - offset + 1);
                offset = index + 2;
            }
            else
            {
                // check if the next part is an argument index
                std::size_t argindex = arguments.size();
                std::size_t startOfArg = index;
                auto ret = std::from_chars(str.data() + index + 1, str.data() + str.size(), argindex);
                index = ret.ptr - str.data();
                // find closing brace
                bool braceError = false;
                std::size_t closeBrace = str.find('}', index);
                if (closeBrace == std::string::npos)
                {
                    braceError = true;
                    closeBrace = str.size();
                }
                // check for format specifier
                if (str[index] == ':')
                    index++;
                // add to vectors
                arguments.emplace_back(std::string(str.substr(index, closeBrace - index)));
                segments.emplace_back(offset, startOfArg - offset, argindex, braceError);
                offset = closeBrace + 1;
            }
            index = str.find('{', offset);
        }

        // PHASE 2 - Recursive variadic template
        index = 0;
        (format_helper(arguments, index, args), ...);

        // PHASE 3 - Stich the string back together
        std::ostringstream oss;
        for (auto& segment : segments)
        {
            oss << str.substr(segment.offset, segment.count);
            if (segment.braceError == true)
            {
                oss << "{ error: no matching '}' found";
            }
            else
            {
                if (segment.argument != std::numeric_limits<std::size_t>::max())
                {
                    if (segment.argument < arguments.size() && arguments[segment.argument].handled)
                        oss << arguments[segment.argument].text;
                    else
                        oss << format("{{ error: invalid arg index: {} }", segment.argument);
                }
            }
        }
        if (offset < str.size())
            oss << str.substr(offset);
        return oss.str();
    }
}


int main()
{
    int a = 5;
    double b = 3.14;
    std::string c("hello");

    // simple arguments
    std::cout << jdt::format("a = {}, b = {}, c = {}\n", a, b, c);

    // arguments with indexes
    std::cout << jdt::format("a = {2}, b = {1}, c = {0}\n", a, b, c);

    // literal open-braces character
    std::cout << jdt::format("let object = {{\"a\": {0}, \"b\": {1}, \"c\": \"{2}\"};\n", a, b, c);

    // too few arguments
    std::cout << jdt::format("a = {0}, b = {1}, c = {2}\n", a, b);

    // invalid argument indexes
    std::cout << jdt::format("a = {0}, b = {10}, c = {2}\n", a, b, c);

    // too many arguments
    std::cout << jdt::format("a = {2}, b = {1}, c = {0}\n", a, b, c, 3, 4, 5);

    // missing closing brace }
    std::cout << jdt::format("a = {2}, b = {1}, c = {0\n", a, b, c);
}
#include <charconv>
#include <iostream>
#include <limits>
#include <sstream>
#include <string>
#include <string_view>
#include <vector>

namespace jdt
{
    struct Segment
    {
        Segment(std::size_t offset, std::size_t count, std::size_t argument = std::numeric_limits<std::size_t>::max(), bool braceError = false)
            : offset{ offset },
            count{ count },
            argument{ argument },
            braceError{ braceError }
        {
        }

        std::size_t offset;
        std::size_t count;
        std::size_t argument;
        bool braceError;
    };

    struct Argument
    {
        Argument(const std::string& format)
            : format{ format },
            handled{ false }
        {
        }

        std::string format;
        std::string text;
        bool handled;
    };

    template<typename T>
    void format_helper(std::vector<Argument>& arguments, std::size_t& index, const T& value)
    {
        if (index < arguments.size())
        {
            Argument& arg = arguments[index];
            std::ostringstream oss;
            oss << value;
            arg.text = oss.str();
            arg.handled = true;
            index++;
        }
    }

    template<typename... Targs>
    std::string format(std::string_view str, Targs...args)
    {
        // PHASE 1 - parse format string
        std::vector<Segment> segments;
        std::vector<Argument> arguments;
        std::size_t offset = 0;
        std::size_t index = str.find('{');
        while (index != std::string::npos)
        {
            // check for literal open-braces character
            if (str[index + 1] == '{')
            {
                segments.emplace_back(offset, index - offset + 1);
                offset = index + 2;
            }
            else
            {
                // check if the next part is an argument index
                std::size_t argindex = arguments.size();
                std::size_t startOfArg = index;
                auto ret = std::from_chars(str.data() + index + 1, str.data() + str.size(), argindex);
                index = ret.ptr - str.data();
                // find closing brace
                bool braceError = false;
                std::size_t closeBrace = str.find('}', index);
                if (closeBrace == std::string::npos)
                {
                    braceError = true;
                    closeBrace = str.size();
                }
                // check for format specifier
                if (str[index] == ':')
                    index++;
                // add to vectors
                arguments.emplace_back(std::string(str.substr(index, closeBrace - index)));
                segments.emplace_back(offset, startOfArg - offset, argindex, braceError);
                offset = closeBrace + 1;
            }
            index = str.find('{', offset);
        }

        // PHASE 2 - Recursive variadic template
        index = 0;
        (format_helper(arguments, index, args), ...);

        // PHASE 3 - Stich the string back together
        std::ostringstream oss;
        for (auto& segment : segments)
        {
            oss << str.substr(segment.offset, segment.count);
            if (segment.braceError == true)
            {
                oss << "{ error: no matching '}' found";
            }
            else
            {
                if (segment.argument != std::numeric_limits<std::size_t>::max())
                {
                    if (segment.argument < arguments.size() && arguments[segment.argument].handled)
                        oss << arguments[segment.argument].text;
                    else
                        oss << format("{{ error: invalid arg index: {} }", segment.argument);
                }
            }
        }
        if (offset < str.size())
            oss << str.substr(offset);
        return oss.str();
    }
}


int main()
{
    int a = 5;
    double b = 3.14;
    std::string c("hello");

    // simple arguments
    std::cout << jdt::format("a = {}, b = {}, c = {}\n", a, b, c);

    // arguments with indexes
    std::cout << jdt::format("a = {2}, b = {1}, c = {0}\n", a, b, c);

    // literal open-braces character
    std::cout << jdt::format("let object = {{\"a\": {0}, \"b\": {1}, \"c\": \"{2}\"};\n", a, b, c);

    // too few arguments
    std::cout << jdt::format("a = {0}, b = {1}, c = {2}\n", a, b);

    // invalid argument indexes
    std::cout << jdt::format("a = {0}, b = {10}, c = {2}\n", a, b, c);

    // too many arguments
    std::cout << jdt::format("a = {2}, b = {1}, c = {0}\n", a, b, c, 3, 4, 5);

    // missing closing brace }
    std::cout << jdt::format("a = {2}, b = {1}, c = {0\n", a, b, c);
}
deleted 776 characters in body
Source Link
jdt
  • 2.5k
  • 6
  • 22
#include <charconv>
#include <iostream>
#include <limits>
#include <stdexcept>
#include <sstream>
#include <string>
#include <string_view>
#include <vector>

namespace jdt
{
    struct Segment
    {
        Segment(std::size_t offset, std::size_t count, std::size_t argument = std::numeric_limits<std::size_t>::max(), bool braceError = false)
            : offset { offset },
            count{ count },
            argument{ argument },
            braceError { braceError }
        {
        }

        std::size_t offset;
        std::size_t count;
        std::size_t argument;
        bool braceError;
    };

    struct Argument
    {
        Argument(const std::string& format)
            : format{ format },
            handled{ false }
        {
        }

        std::string format;
        std::string text;
        bool handled;
    };

    struct ArgumentList
    {
        std::vector<Argument> args;
        std::size_t index;
    };

    template<typename T>
    void format_helper(ArgumentList&std::vector<Argument>& arguments, std::size_t& index, const T& value)
    {
        if (arguments.index < arguments.args.size())
        {
            Argument& arg = arguments.args[arguments.index];arguments[index];
            std::ostringstream oss;
            oss << value;
            arg.text = oss.str();
            arg.handled = true;
            arguments.index++;
        }
    }
}

    template<typename... Targs>
    std::string format(std::string_view str, Targs...args)
{
    // PHASE 1 - parse format string
    std::vector<jdt::Segment> segments;
    jdt::ArgumentList arguments;
    std::size_t offset = 0;
    std::size_t index = str.find('{');
    while (index != std::string::npos)
    {
        // check forPHASE literal1 open-braces character
  parse format string
    if (str[index + 1] ==std::vector<Segment> '{')segments;
        {
     std::vector<Argument> arguments;
      segments.emplace_back(offset, index -std::size_t offset += 1);0;
            offset =std::size_t index += 2;str.find('{');
        }
     while (index != elsestd::string::npos)
        {
            // check if the next part is an argument index
            std::size_t argindex = arguments.args.size();
            std::size_tfor startOfArgliteral =open-braces index;character
            auto ret = std::from_chars(str.data() + index + 1,if str.data()str[index + str.size(), argindex);
            index = ret.ptr - str.data();
            // find closing brace
            bool braceError = false;
            std::size_t closeBrace1] === str.find('}{', index);
            if (closeBrace == std::string::npos)
            {
                braceErrorsegments.emplace_back(offset, =index true;- offset + 1);
                closeBraceoffset = str.size();index + 2;
            }
            else
            {
                // check if the next part is an argument index
                std::size_t argindex = arguments.size();
                std::size_t startOfArg = index;
                auto ret = std::from_chars(str.data() + index + 1, str.data() + str.size(), argindex);
                index = ret.ptr - str.data();
                // find closing brace
                bool braceError = false;
                std::size_t closeBrace = str.find('}', index);
                if (closeBrace == std::string::npos)
                {
                    braceError = true;
                    closeBrace = str.size();
                }
                // check for format specifier
                if (str[index] == ':')
                    index++;
                // add to vectors
                arguments.args.emplace_back(std::string(str.substr(index, closeBrace - index)));
                segments.emplace_back(offset, startOfArg - offset, argindex, braceError);
                offset = closeBrace + 1;
            }
            index = str.find('{', offset);
        }
        index = str.find('{', offset);
    }

        // PHASE 2 - Recursive variadic template
    arguments.    index = 0;
        (format_helper(arguments, index, args), ...);

        // PHASE 3 - Stich the string back together
    std::ostringstream oss;
    for (auto& segment std: segments)
   :ostringstream {oss;
        oss <<for str.substr(segment.offset, segment.count);
        ifauto& (segment.braceError == true)
        {
            oss << "{ error: no matching '}' found";
        }
        elsesegments)
        {
            ifoss << str.substr(segment.argumentoffset, !=segment.count);
 std::numeric_limits<std::size_t>::max           if ()segment.braceError == true)
            {
                if (segment.argument < arguments.args.size() && arguments.args[segment.argument].handled)
                    oss << arguments.args[segment.argument].text;
                else
                    oss << format("{{ error: invalid arg index:no {}matching '}",' segment.argument);
found";
            }
            else
            {
                if (segment.argument != std::numeric_limits<std::size_t>::max())
                {
                    if (segment.argument < arguments.size() && arguments[segment.argument].handled)
                        oss << arguments[segment.argument].text;
                    else
                        oss << format("{{ error: invalid arg index: {} }", segment.argument);
                }
            }
        }
        if (offset < str.size())
            oss << str.substr(offset);
        return oss.str();
    }
    if (offset < str.size())
        oss << str.substr(offset);
    return oss.str();
}


int main()
{
    int a = 5;
    double b = 3.14;
    std::string c("hello");

    // simple arguments
    std::cout << jdt::format("a = {}, b = {}, c = {}\n", a, b, c);

    // arguments with indexes
    std::cout << jdt::format("a = {2}, b = {1}, c = {0}\n", a, b, c);

    // literal open-braces character
    std::cout << jdt::format("let object = {{\"a\": {0}, \"b\": {1}, \"c\": \"{2}\"};\n", a, b, c);

    // too few arguments
    std::cout << jdt::format("a = {0}, b = {1}, c = {2}\n", a, b);

    // invalid argument indexes
    std::cout << jdt::format("a = {0}, b = {10}, c = {2}\n", a, b, c);

    // too many arguments
    std::cout << jdt::format("a = {2}, b = {1}, c = {0}\n", a, b, c, 3, 4, 5);

    // missing closing brace }
    std::cout << jdt::format("a = {2}, b = {1}, c = {0\n", a, b, c);
}
#include <charconv>
#include <iostream>
#include <limits>
#include <sstream>
#include <string>
#include <string_view>
#include <vector>

namespace jdt
{
    struct Segment
    {
        Segment(std::size_t offset, std::size_t count, std::size_t argument = std::numeric_limits<std::size_t>::max(), bool braceError = false)
            : offset { offset },
            count{ count },
            argument{ argument },
            braceError { braceError }
        {
        }

        std::size_t offset;
        std::size_t count;
        std::size_t argument;
        bool braceError;
    };

    struct Argument
    {
        Argument(const std::string& format)
            : format{ format },
            handled{ false }
        {
        }

        std::string format;
        std::string text;
        bool handled;
    };

    struct ArgumentList
    {
        std::vector<Argument> args;
        std::size_t index;
    };

    template<typename T>
    void format_helper(ArgumentList& arguments, const T& value)
    {
        if (arguments.index < arguments.args.size())
        {
            Argument& arg = arguments.args[arguments.index];
            std::ostringstream oss;
            oss << value;
            arg.text = oss.str();
            arg.handled = true;
            arguments.index++;
        }
    }
}

template<typename... Targs>
std::string format(std::string_view str, Targs...args)
{
    // PHASE 1 - parse format string
    std::vector<jdt::Segment> segments;
    jdt::ArgumentList arguments;
    std::size_t offset = 0;
    std::size_t index = str.find('{');
    while (index != std::string::npos)
    {
        // check for literal open-braces character
        if (str[index + 1] == '{')
        {
            segments.emplace_back(offset, index - offset + 1);
            offset = index + 2;
        }
        else
        {
            // check if the next part is an argument index
            std::size_t argindex = arguments.args.size();
            std::size_t startOfArg = index;
            auto ret = std::from_chars(str.data() + index + 1, str.data() + str.size(), argindex);
            index = ret.ptr - str.data();
            // find closing brace
            bool braceError = false;
            std::size_t closeBrace = str.find('}', index);
            if (closeBrace == std::string::npos)
            {
                braceError = true;
                closeBrace = str.size();
            }
            // check for format specifier
            if (str[index] == ':')
                index++;
            // add to vectors
            arguments.args.emplace_back(std::string(str.substr(index, closeBrace - index)));
            segments.emplace_back(offset, startOfArg - offset, argindex, braceError);
            offset = closeBrace + 1;
        }
        index = str.find('{', offset);
    }

    // PHASE 2 - Recursive variadic template
    arguments.index = 0;
    (format_helper(arguments, args), ...);

    // PHASE 3 - Stich the string back together
    std::ostringstream oss;
    for (auto& segment : segments)
    {
        oss << str.substr(segment.offset, segment.count);
        if (segment.braceError == true)
        {
            oss << "{ error: no matching '}' found";
        }
        else
        {
            if (segment.argument != std::numeric_limits<std::size_t>::max())
            {
                if (segment.argument < arguments.args.size() && arguments.args[segment.argument].handled)
                    oss << arguments.args[segment.argument].text;
                else
                    oss << format("{{ error: invalid arg index: {} }", segment.argument);

            }
        }
    }
    if (offset < str.size())
        oss << str.substr(offset);
    return oss.str();
}


int main()
{
    int a = 5;
    double b = 3.14;
    std::string c("hello");

    // simple arguments
    std::cout << format("a = {}, b = {}, c = {}\n", a, b, c);

    // arguments with indexes
    std::cout << format("a = {2}, b = {1}, c = {0}\n", a, b, c);

    // literal open-braces character
    std::cout << format("let object = {{\"a\": {0}, \"b\": {1}, \"c\": \"{2}\"};\n", a, b, c);

    // too few arguments
    std::cout << format("a = {0}, b = {1}, c = {2}\n", a, b);

    // invalid argument indexes
    std::cout << format("a = {0}, b = {10}, c = {2}\n", a, b, c);

    // too many arguments
    std::cout << format("a = {2}, b = {1}, c = {0}\n", a, b, c, 3, 4, 5);

    // missing closing brace }
    std::cout << format("a = {2}, b = {1}, c = {0\n", a, b, c);
}
#include <charconv>
#include <iostream>
#include <limits>
#include <stdexcept>
#include <sstream>
#include <string>
#include <string_view>
#include <vector>

namespace jdt
{
    struct Segment
    {
        Segment(std::size_t offset, std::size_t count, std::size_t argument = std::numeric_limits<std::size_t>::max(), bool braceError = false)
            : offset{ offset },
            count{ count },
            argument{ argument },
            braceError{ braceError }
        {
        }

        std::size_t offset;
        std::size_t count;
        std::size_t argument;
        bool braceError;
    };

    struct Argument
    {
        Argument(const std::string& format)
            : format{ format },
            handled{ false }
        {
        }

        std::string format;
        std::string text;
        bool handled;
    };

    template<typename T>
    void format_helper(std::vector<Argument>& arguments, std::size_t& index, const T& value)
    {
        if (index < arguments.size())
        {
            Argument& arg = arguments[index];
            std::ostringstream oss;
            oss << value;
            arg.text = oss.str();
            arg.handled = true;
            index++;
        }
    }

    template<typename... Targs>
    std::string format(std::string_view str, Targs...args)
    {
        // PHASE 1 - parse format string
        std::vector<Segment> segments;
        std::vector<Argument> arguments;
        std::size_t offset = 0;
        std::size_t index = str.find('{');
        while (index != std::string::npos)
        {
            // check for literal open-braces character
            if (str[index + 1] == '{')
            {
                segments.emplace_back(offset, index - offset + 1);
                offset = index + 2;
            }
            else
            {
                // check if the next part is an argument index
                std::size_t argindex = arguments.size();
                std::size_t startOfArg = index;
                auto ret = std::from_chars(str.data() + index + 1, str.data() + str.size(), argindex);
                index = ret.ptr - str.data();
                // find closing brace
                bool braceError = false;
                std::size_t closeBrace = str.find('}', index);
                if (closeBrace == std::string::npos)
                {
                    braceError = true;
                    closeBrace = str.size();
                }
                // check for format specifier
                if (str[index] == ':')
                    index++;
                // add to vectors
                arguments.emplace_back(std::string(str.substr(index, closeBrace - index)));
                segments.emplace_back(offset, startOfArg - offset, argindex, braceError);
                offset = closeBrace + 1;
            }
            index = str.find('{', offset);
        }

        // PHASE 2 - Recursive variadic template
        index = 0;
        (format_helper(arguments, index, args), ...);

        // PHASE 3 - Stich the string back together
        std::ostringstream oss;
        for (auto& segment : segments)
        {
            oss << str.substr(segment.offset, segment.count);
            if (segment.braceError == true)
            {
                oss << "{ error: no matching '}' found";
            }
            else
            {
                if (segment.argument != std::numeric_limits<std::size_t>::max())
                {
                    if (segment.argument < arguments.size() && arguments[segment.argument].handled)
                        oss << arguments[segment.argument].text;
                    else
                        oss << format("{{ error: invalid arg index: {} }", segment.argument);
                }
            }
        }
        if (offset < str.size())
            oss << str.substr(offset);
        return oss.str();
    }
}


int main()
{
    int a = 5;
    double b = 3.14;
    std::string c("hello");

    // simple arguments
    std::cout << jdt::format("a = {}, b = {}, c = {}\n", a, b, c);

    // arguments with indexes
    std::cout << jdt::format("a = {2}, b = {1}, c = {0}\n", a, b, c);

    // literal open-braces character
    std::cout << jdt::format("let object = {{\"a\": {0}, \"b\": {1}, \"c\": \"{2}\"};\n", a, b, c);

    // too few arguments
    std::cout << jdt::format("a = {0}, b = {1}, c = {2}\n", a, b);

    // invalid argument indexes
    std::cout << jdt::format("a = {0}, b = {10}, c = {2}\n", a, b, c);

    // too many arguments
    std::cout << jdt::format("a = {2}, b = {1}, c = {0}\n", a, b, c, 3, 4, 5);

    // missing closing brace }
    std::cout << jdt::format("a = {2}, b = {1}, c = {0\n", a, b, c);
}
edited body
Source Link
jdt
  • 2.5k
  • 6
  • 22
#include <charconv>
#include <iostream>
#include <limits>
#include <sstream>
#include <string>
#include <string_view>
#include <vector>

namespace jdt
{
    struct Segment
    {
        Segment(std::size_t offset, std::size_t count, std::size_t argument = std::numeric_limits<std::size_t>::max(), bool braceError = false)
            : offset { offset },
            count{ count },
            argument{ argument },
            braceError { braceError }
        {
        }

        std::size_t offset;
        std::size_t count;
        std::size_t argument;
        bool braceError;
    };

    struct Argument
    {
        Argument(const std::string& format)
            : format{ format },
            handled{ false }
        {
        }

        std::string format;
        std::string text;
        bool handled;
    };

    struct ArgumentList
    {
        std::vector<Argument> args;
        std::size_t index;
    };

    template<typename T>
    void format_helper(ArgumentList& arguments, const T& value)
    {
        if (arguments.index < arguments.args.size())
        {
            Argument& arg = arguments.args[arguments.index];
            std::ostringstream oss;
            oss << value;
            arg.text = oss.str();
            arg.handled = true;
            arguments.index++;
        }
    }
}

template<typename... Targs>
std::string format(std::string_view str, Targs...args)
{
    // PHASE 1 - parse format string
    std::vector<jdt::Segment> segments;
    jdt::ArgumentList arguments;
    std::size_t offset = 0;
    std::size_t index = str.find('{');
    while (index != std::string::npos)
    {
        // check for literal open-braces character
        if (str[index + 1] == '{')
        {
            segments.emplace_back(offset, index - offset + 1);
            offset = index + 2;
        }
        else
        {
            // check if the next part is an argument index
            std::size_t argindex = arguments.args.size();
            std::size_t startOfArg = index;
            auto ret = std::from_chars(str.data() + index + 1, str.data() + str.size(), argindex);
            index = ret.ptr - str.data();
            // find closing brace
            bool braceError = false;
            std::size_t closeBrace = str.find('}', index);
            if (closeBrace == std::string::npos)
            {
                braceError = true;
                closeBrace = str.size();
            }
            // check for format specifier
            if (str[index] == ':')
                index++;
            // add to vectors
            arguments.args.emplace_back(std::string(str.substr(index, closeBrace - index)));
            segments.emplace_back(offset, startOfArg - offset, argindex, braceError);
            offset = closeBrace + 1;
        }
        index = str.find('{', offset);
    }

    // PHASE 2 - Recursive variadic template
    arguments.index = 0;
    (format_helper(arguments, args), ...);

    // PHASE 23 - Stich the string back together
    std::ostringstream oss;
    for (auto& segment : segments)
    {
        oss << str.substr(segment.offset, segment.count);
        if (segment.braceError == true)
        {
            oss << "{ error: no matching '}' found";
        }
        else
        {
            if (segment.argument != std::numeric_limits<std::size_t>::max())
            {
                if (segment.argument < arguments.args.size() && arguments.args[segment.argument].handled)
                    oss << arguments.args[segment.argument].text;
                else
                    oss << format("{{ error: invalid arg index: {} }", segment.argument);

            }
        }
    }
    if (offset < str.size())
        oss << str.substr(offset);
    return oss.str();
}


int main()
{
    int a = 5;
    double b = 3.14;
    std::string c("hello");

    // simple arguments
    std::cout << format("a = {}, b = {}, c = {}\n", a, b, c);

    // arguments with indexes
    std::cout << format("a = {2}, b = {1}, c = {0}\n", a, b, c);

    // literal open-braces character
    std::cout << format("let object = {{\"a\": {0}, \"b\": {1}, \"c\": \"{2}\"};\n", a, b, c);

    // too few arguments
    std::cout << format("a = {0}, b = {1}, c = {2}\n", a, b);

    // invalid argument indexes
    std::cout << format("a = {0}, b = {10}, c = {2}\n", a, b, c);

    // too many arguments
    std::cout << format("a = {2}, b = {1}, c = {0}\n", a, b, c, 3, 4, 5);

    // missing closing brace }
    std::cout << format("a = {2}, b = {1}, c = {0\n", a, b, c);
}
#include <charconv>
#include <iostream>
#include <limits>
#include <sstream>
#include <string>
#include <string_view>
#include <vector>

namespace jdt
{
    struct Segment
    {
        Segment(std::size_t offset, std::size_t count, std::size_t argument = std::numeric_limits<std::size_t>::max(), bool braceError = false)
            : offset { offset },
            count{ count },
            argument{ argument },
            braceError { braceError }
        {
        }

        std::size_t offset;
        std::size_t count;
        std::size_t argument;
        bool braceError;
    };

    struct Argument
    {
        Argument(const std::string& format)
            : format{ format },
            handled{ false }
        {
        }

        std::string format;
        std::string text;
        bool handled;
    };

    struct ArgumentList
    {
        std::vector<Argument> args;
        std::size_t index;
    };

    template<typename T>
    void format_helper(ArgumentList& arguments, const T& value)
    {
        if (arguments.index < arguments.args.size())
        {
            Argument& arg = arguments.args[arguments.index];
            std::ostringstream oss;
            oss << value;
            arg.text = oss.str();
            arg.handled = true;
            arguments.index++;
        }
    }
}

template<typename... Targs>
std::string format(std::string_view str, Targs...args)
{
    // PHASE 1 - parse format string
    std::vector<jdt::Segment> segments;
    jdt::ArgumentList arguments;
    std::size_t offset = 0;
    std::size_t index = str.find('{');
    while (index != std::string::npos)
    {
        // check for literal open-braces character
        if (str[index + 1] == '{')
        {
            segments.emplace_back(offset, index - offset + 1);
            offset = index + 2;
        }
        else
        {
            // check if the next part is an argument index
            std::size_t argindex = arguments.args.size();
            std::size_t startOfArg = index;
            auto ret = std::from_chars(str.data() + index + 1, str.data() + str.size(), argindex);
            index = ret.ptr - str.data();
            // find closing brace
            bool braceError = false;
            std::size_t closeBrace = str.find('}', index);
            if (closeBrace == std::string::npos)
            {
                braceError = true;
                closeBrace = str.size();
            }
            // check for format specifier
            if (str[index] == ':')
                index++;
            // add to vectors
            arguments.args.emplace_back(std::string(str.substr(index, closeBrace - index)));
            segments.emplace_back(offset, startOfArg - offset, argindex, braceError);
            offset = closeBrace + 1;
        }
        index = str.find('{', offset);
    }

    // PHASE 2 - Recursive variadic template
    arguments.index = 0;
    (format_helper(arguments, args), ...);

    // PHASE 2 - Stich the string back together
    std::ostringstream oss;
    for (auto& segment : segments)
    {
        oss << str.substr(segment.offset, segment.count);
        if (segment.braceError == true)
        {
            oss << "{ error: no matching '}' found";
        }
        else
        {
            if (segment.argument != std::numeric_limits<std::size_t>::max())
            {
                if (segment.argument < arguments.args.size() && arguments.args[segment.argument].handled)
                    oss << arguments.args[segment.argument].text;
                else
                    oss << format("{{ error: invalid arg index: {} }", segment.argument);

            }
        }
    }
    if (offset < str.size())
        oss << str.substr(offset);
    return oss.str();
}


int main()
{
    int a = 5;
    double b = 3.14;
    std::string c("hello");

    // simple arguments
    std::cout << format("a = {}, b = {}, c = {}\n", a, b, c);

    // arguments with indexes
    std::cout << format("a = {2}, b = {1}, c = {0}\n", a, b, c);

    // literal open-braces character
    std::cout << format("let object = {{\"a\": {0}, \"b\": {1}, \"c\": \"{2}\"};\n", a, b, c);

    // too few arguments
    std::cout << format("a = {0}, b = {1}, c = {2}\n", a, b);

    // invalid argument indexes
    std::cout << format("a = {0}, b = {10}, c = {2}\n", a, b, c);

    // too many arguments
    std::cout << format("a = {2}, b = {1}, c = {0}\n", a, b, c, 3, 4, 5);

    // missing closing brace }
    std::cout << format("a = {2}, b = {1}, c = {0\n", a, b, c);
}
#include <charconv>
#include <iostream>
#include <limits>
#include <sstream>
#include <string>
#include <string_view>
#include <vector>

namespace jdt
{
    struct Segment
    {
        Segment(std::size_t offset, std::size_t count, std::size_t argument = std::numeric_limits<std::size_t>::max(), bool braceError = false)
            : offset { offset },
            count{ count },
            argument{ argument },
            braceError { braceError }
        {
        }

        std::size_t offset;
        std::size_t count;
        std::size_t argument;
        bool braceError;
    };

    struct Argument
    {
        Argument(const std::string& format)
            : format{ format },
            handled{ false }
        {
        }

        std::string format;
        std::string text;
        bool handled;
    };

    struct ArgumentList
    {
        std::vector<Argument> args;
        std::size_t index;
    };

    template<typename T>
    void format_helper(ArgumentList& arguments, const T& value)
    {
        if (arguments.index < arguments.args.size())
        {
            Argument& arg = arguments.args[arguments.index];
            std::ostringstream oss;
            oss << value;
            arg.text = oss.str();
            arg.handled = true;
            arguments.index++;
        }
    }
}

template<typename... Targs>
std::string format(std::string_view str, Targs...args)
{
    // PHASE 1 - parse format string
    std::vector<jdt::Segment> segments;
    jdt::ArgumentList arguments;
    std::size_t offset = 0;
    std::size_t index = str.find('{');
    while (index != std::string::npos)
    {
        // check for literal open-braces character
        if (str[index + 1] == '{')
        {
            segments.emplace_back(offset, index - offset + 1);
            offset = index + 2;
        }
        else
        {
            // check if the next part is an argument index
            std::size_t argindex = arguments.args.size();
            std::size_t startOfArg = index;
            auto ret = std::from_chars(str.data() + index + 1, str.data() + str.size(), argindex);
            index = ret.ptr - str.data();
            // find closing brace
            bool braceError = false;
            std::size_t closeBrace = str.find('}', index);
            if (closeBrace == std::string::npos)
            {
                braceError = true;
                closeBrace = str.size();
            }
            // check for format specifier
            if (str[index] == ':')
                index++;
            // add to vectors
            arguments.args.emplace_back(std::string(str.substr(index, closeBrace - index)));
            segments.emplace_back(offset, startOfArg - offset, argindex, braceError);
            offset = closeBrace + 1;
        }
        index = str.find('{', offset);
    }

    // PHASE 2 - Recursive variadic template
    arguments.index = 0;
    (format_helper(arguments, args), ...);

    // PHASE 3 - Stich the string back together
    std::ostringstream oss;
    for (auto& segment : segments)
    {
        oss << str.substr(segment.offset, segment.count);
        if (segment.braceError == true)
        {
            oss << "{ error: no matching '}' found";
        }
        else
        {
            if (segment.argument != std::numeric_limits<std::size_t>::max())
            {
                if (segment.argument < arguments.args.size() && arguments.args[segment.argument].handled)
                    oss << arguments.args[segment.argument].text;
                else
                    oss << format("{{ error: invalid arg index: {} }", segment.argument);

            }
        }
    }
    if (offset < str.size())
        oss << str.substr(offset);
    return oss.str();
}


int main()
{
    int a = 5;
    double b = 3.14;
    std::string c("hello");

    // simple arguments
    std::cout << format("a = {}, b = {}, c = {}\n", a, b, c);

    // arguments with indexes
    std::cout << format("a = {2}, b = {1}, c = {0}\n", a, b, c);

    // literal open-braces character
    std::cout << format("let object = {{\"a\": {0}, \"b\": {1}, \"c\": \"{2}\"};\n", a, b, c);

    // too few arguments
    std::cout << format("a = {0}, b = {1}, c = {2}\n", a, b);

    // invalid argument indexes
    std::cout << format("a = {0}, b = {10}, c = {2}\n", a, b, c);

    // too many arguments
    std::cout << format("a = {2}, b = {1}, c = {0}\n", a, b, c, 3, 4, 5);

    // missing closing brace }
    std::cout << format("a = {2}, b = {1}, c = {0\n", a, b, c);
}
Source Link
jdt
  • 2.5k
  • 6
  • 22
Loading