Skip to main content
deleted 2 characters in body
Source Link
jdt
  • 2.5k
  • 6
  • 22
#include <bitset>
#include <charconv>
#include <exception>
#include <iostream>
#include <sstream>
#include <string>
#include <string_view>

namespace jdt
{
    template<typename T>
    void toBinary(std::ostream& oss, const T& x)
    {
        oss << std::bitset<sizeof(T) * 8>(x);
    }

    void toBinary(std::ostream& oss, const double& x)
    {
        union 
        {
            double a;
            unsigned long long b;
        } data;
        data.a = x;
        toBinary(oss, data.b);
    }

    void toBinary(std::ostream& oss, const float& x)
    {
        union
        {
            float a;
            unsigned int b;
        } data;
        data.a = x;
        toBinary(oss, data.b);
    }

    void toBinary(std::ostream& oss, const std::string& x)
    {
        for (auto c : x)
            toBinary(oss, c);
    }

    void toBinary(std::ostream& oss, const char* x)
    {
        for (; *x; x++)
            toBinary(oss, *x);
    }

    template<typename T>
    void format_helper(std::ostream& oss, std::string_view fmt, const T& value)
    {
        if (fmt.find('b') != std::string::npos)
        {
            toBinary(oss, value);
        }
        else
        {
            oss << value;
        }
    }

    template<typename... Targs>
    static bool format_one(std::ostream& oss, size_t i, std::string_view fmt, const Targs&... args) {
        auto visitor = [&](const auto& arg) {
            if (!i--) {
                format_helper(oss, fmt, arg);
                return true;
            }
            else {
                return false;
            }
        };
        return (visitor(args) || ...);
    }

    template<typename... Targs>
    std::string format(std::string_view str, const Targs&... args)
    {
        std::ostringstream oss;
        std::size_t offset = 0;
        std::size_t index = str.find('{');
        std::size_t argumentCounter = 0;
        while (index != std::string::npos)
        {
            // check for literal open-braces character
            if (str[index + 1] == '{')
            {
                oss.write(str.data() + offset, index - offset + 1);
                offset = index + 2;
            }
            else
            {
                // check if the next part is an argument index
                std::size_t argindex = argumentCounter++;
                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)
                    throw std::runtime_error(format("error in string: {0}, column: {1}: '{{': no matching token found ", std::string(str), startOfArg).c_str());
                // check for format specifier
                if (str[index] == ':')
                    index++;
                oss.write(str.data() + offset, startOfArg - offset);
                if (!format_one(oss, argindex, str.substr(index, closeBrace - index), args...))
                    throw std::runtime_error(format("error in string: {0}, column: {1}: invalid argument index: {2}", std::string(str), startOfArg, argindex).c_str());
                if (argindex < 0)
                    std::cout << argindex << "\n";
                offset = closeBrace + 1;
            }
            index = str.find('{', offset);
        }
        if (offset < str.size())
            oss << str.substr(offset);
        return oss.str();
    }
}

int main()
{
    int a = 5;
    double b = 3.14;
    std::string c("hello");
    char d = 'Z';
    short e = 16634;
    float f = 123.456f;

    // 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);

    // binary formats
    std::cout << jdt::format("char: {0} = {0:b}\n", d);
    std::cout << jdt::format("short: {0} = {0:b}\n", e);
    std::cout << jdt::format("int: {0} = {0:b}\n", a);
    std::cout << jdt::format("double: {0} = {0:b}\n", b);
    std::cout << jdt::format("float: {0} = {0:b}\n", f);
    std::cout << jdt::format("std::string: {0} = {0:b}\n", c, c);
    std::cout << jdt::format("char*: {0} = {0:b}\n", "ABCDEFG");

    // 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 <bitset>
#include <charconv>
#include <exception>
#include <iostream>
#include <sstream>
#include <string>
#include <string_view>

namespace jdt
{
    template<typename T>
    void toBinary(std::ostream& oss, const T& x)
    {
        oss << std::bitset<sizeof(T) * 8>(x);
    }

    void toBinary(std::ostream& oss, const double& x)
    {
        union 
        {
            double a;
            unsigned long long b;
        } data;
        data.a = x;
        toBinary(oss, data.b);
    }

    void toBinary(std::ostream& oss, const float& x)
    {
        union
        {
            float a;
            unsigned int b;
        } data;
        data.a = x;
        toBinary(oss, data.b);
    }

    void toBinary(std::ostream& oss, const std::string& x)
    {
        for (auto c : x)
            toBinary(oss, c);
    }

    void toBinary(std::ostream& oss, const char* x)
    {
        for (; *x; x++)
            toBinary(oss, *x);
    }

    template<typename T>
    void format_helper(std::ostream& oss, std::string_view fmt, const T& value)
    {
        if (fmt.find('b') != std::string::npos)
        {
            toBinary(oss, value);
        }
        else
        {
            oss << value;
        }
    }

    template<typename... Targs>
    static bool format_one(std::ostream& oss, size_t i, std::string_view fmt, const Targs&... args) {
        auto visitor = [&](const auto& arg) {
            if (!i--) {
                format_helper(oss, fmt, arg);
                return true;
            }
            else {
                return false;
            }
        };
        return (visitor(args) || ...);
    }

    template<typename... Targs>
    std::string format(std::string_view str, const Targs&... args)
    {
        std::ostringstream oss;
        std::size_t offset = 0;
        std::size_t index = str.find('{');
        std::size_t argumentCounter = 0;
        while (index != std::string::npos)
        {
            // check for literal open-braces character
            if (str[index + 1] == '{')
            {
                oss.write(str.data() + offset, index - offset + 1);
                offset = index + 2;
            }
            else
            {
                // check if the next part is an argument index
                std::size_t argindex = argumentCounter++;
                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)
                    throw std::runtime_error(format("error in string: {0}, column: {1}: '{{': no matching token found ", std::string(str), startOfArg).c_str());
                // check for format specifier
                if (str[index] == ':')
                    index++;
                oss.write(str.data() + offset, startOfArg - offset);
                if (!format_one(oss, argindex, str.substr(index, closeBrace - index), args...))
                    throw std::runtime_error(format("error in string: {0}, column: {1}: invalid argument index: {2}", std::string(str), startOfArg, argindex).c_str());
                if (argindex < 0)
                    std::cout << argindex << "\n";
                offset = closeBrace + 1;
            }
            index = str.find('{', offset);
        }
        if (offset < str.size())
            oss << str.substr(offset);
        return oss.str();
    }
}

int main()
{
    int a = 5;
    double b = 3.14;
    std::string c("hello");
    char d = 'Z';
    short e = 16634;
    float f = 123.456f;

    // 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);

    // binary formats
    std::cout << jdt::format("char: {0} = {0:b}\n", d);
    std::cout << jdt::format("short: {0} = {0:b}\n", e);
    std::cout << jdt::format("int: {0} = {0:b}\n", a);
    std::cout << jdt::format("double: {0} = {0:b}\n", b);
    std::cout << jdt::format("float: {0} = {0:b}\n", f);
    std::cout << jdt::format("std::string: {0} = {0:b}\n", c, c);
    std::cout << jdt::format("char*: {0} = {0:b}\n", "ABCDEFG");

    // 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 <bitset>
#include <charconv>
#include <exception>
#include <iostream>
#include <sstream>
#include <string>
#include <string_view>

namespace jdt
{
    template<typename T>
    void toBinary(std::ostream& oss, const T& x)
    {
        oss << std::bitset<sizeof(T) * 8>(x);
    }

    void toBinary(std::ostream& oss, const double& x)
    {
        union 
        {
            double a;
            unsigned long long b;
        } data;
        data.a = x;
        toBinary(oss, data.b);
    }

    void toBinary(std::ostream& oss, const float& x)
    {
        union
        {
            float a;
            unsigned int b;
        } data;
        data.a = x;
        toBinary(oss, data.b);
    }

    void toBinary(std::ostream& oss, const std::string& x)
    {
        for (auto c : x)
            toBinary(oss, c);
    }

    void toBinary(std::ostream& oss, const char* x)
    {
        for (; *x; x++)
            toBinary(oss, *x);
    }

    template<typename T>
    void format_helper(std::ostream& oss, std::string_view fmt, const T& value)
    {
        if (fmt.find('b') != std::string::npos)
        {
            toBinary(oss, value);
        }
        else
        {
            oss << value;
        }
    }

    template<typename... Targs>
    static bool format_one(std::ostream& oss, size_t i, std::string_view fmt, const Targs&... args) {
        auto visitor = [&](const auto& arg) {
            if (!i--) {
                format_helper(oss, fmt, arg);
                return true;
            }
            else {
                return false;
            }
        };
        return (visitor(args) || ...);
    }

    template<typename... Targs>
    std::string format(std::string_view str, const Targs&... args)
    {
        std::ostringstream oss;
        std::size_t offset = 0;
        std::size_t index = str.find('{');
        std::size_t argumentCounter = 0;
        while (index != std::string::npos)
        {
            // check for literal open-braces character
            if (str[index + 1] == '{')
            {
                oss.write(str.data() + offset, index - offset + 1);
                offset = index + 2;
            }
            else
            {
                // check if the next part is an argument index
                std::size_t argindex = argumentCounter++;
                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)
                    throw std::runtime_error(format("error in string: {0}, column: {1}: '{{': no matching token found ", std::string(str), startOfArg).c_str());
                // check for format specifier
                if (str[index] == ':')
                    index++;
                oss.write(str.data() + offset, startOfArg - offset);
                if (!format_one(oss, argindex, str.substr(index, closeBrace - index), args...))
                    throw std::runtime_error(format("error in string: {0}, column: {1}: invalid argument index: {2}", std::string(str), startOfArg, argindex).c_str());
                if (argindex < 0)
                    std::cout << argindex << "\n";
                offset = closeBrace + 1;
            }
            index = str.find('{', offset);
        }
        if (offset < str.size())
            oss << str.substr(offset);
        return oss.str();
    }
}

int main()
{
    int a = 5;
    double b = 3.14;
    std::string c("hello");
    char d = 'Z';
    short e = 16634;
    float f = 123.456f;

    // 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);

    // binary formats
    std::cout << jdt::format("char: {0} = {0:b}\n", d);
    std::cout << jdt::format("short: {0} = {0:b}\n", e);
    std::cout << jdt::format("int: {0} = {0:b}\n", a);
    std::cout << jdt::format("double: {0} = {0:b}\n", b);
    std::cout << jdt::format("float: {0} = {0:b}\n", f);
    std::cout << jdt::format("std::string: {0} = {0:b}\n",  c);
    std::cout << jdt::format("char*: {0} = {0:b}\n", "ABCDEFG");

    // 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);
}
Source Link
jdt
  • 2.5k
  • 6
  • 22

Implementing std::format - Part 3

This is a follow-up to this question.

Here I’m trying to do something useful with the format specifier. I guess I should have started with width and precision but foolishly decided to first try my hand at binary formatting. So here we go…

#include <bitset>
#include <charconv>
#include <exception>
#include <iostream>
#include <sstream>
#include <string>
#include <string_view>

namespace jdt
{
    template<typename T>
    void toBinary(std::ostream& oss, const T& x)
    {
        oss << std::bitset<sizeof(T) * 8>(x);
    }

    void toBinary(std::ostream& oss, const double& x)
    {
        union 
        {
            double a;
            unsigned long long b;
        } data;
        data.a = x;
        toBinary(oss, data.b);
    }

    void toBinary(std::ostream& oss, const float& x)
    {
        union
        {
            float a;
            unsigned int b;
        } data;
        data.a = x;
        toBinary(oss, data.b);
    }

    void toBinary(std::ostream& oss, const std::string& x)
    {
        for (auto c : x)
            toBinary(oss, c);
    }

    void toBinary(std::ostream& oss, const char* x)
    {
        for (; *x; x++)
            toBinary(oss, *x);
    }

    template<typename T>
    void format_helper(std::ostream& oss, std::string_view fmt, const T& value)
    {
        if (fmt.find('b') != std::string::npos)
        {
            toBinary(oss, value);
        }
        else
        {
            oss << value;
        }
    }

    template<typename... Targs>
    static bool format_one(std::ostream& oss, size_t i, std::string_view fmt, const Targs&... args) {
        auto visitor = [&](const auto& arg) {
            if (!i--) {
                format_helper(oss, fmt, arg);
                return true;
            }
            else {
                return false;
            }
        };
        return (visitor(args) || ...);
    }

    template<typename... Targs>
    std::string format(std::string_view str, const Targs&... args)
    {
        std::ostringstream oss;
        std::size_t offset = 0;
        std::size_t index = str.find('{');
        std::size_t argumentCounter = 0;
        while (index != std::string::npos)
        {
            // check for literal open-braces character
            if (str[index + 1] == '{')
            {
                oss.write(str.data() + offset, index - offset + 1);
                offset = index + 2;
            }
            else
            {
                // check if the next part is an argument index
                std::size_t argindex = argumentCounter++;
                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)
                    throw std::runtime_error(format("error in string: {0}, column: {1}: '{{': no matching token found ", std::string(str), startOfArg).c_str());
                // check for format specifier
                if (str[index] == ':')
                    index++;
                oss.write(str.data() + offset, startOfArg - offset);
                if (!format_one(oss, argindex, str.substr(index, closeBrace - index), args...))
                    throw std::runtime_error(format("error in string: {0}, column: {1}: invalid argument index: {2}", std::string(str), startOfArg, argindex).c_str());
                if (argindex < 0)
                    std::cout << argindex << "\n";
                offset = closeBrace + 1;
            }
            index = str.find('{', offset);
        }
        if (offset < str.size())
            oss << str.substr(offset);
        return oss.str();
    }
}

int main()
{
    int a = 5;
    double b = 3.14;
    std::string c("hello");
    char d = 'Z';
    short e = 16634;
    float f = 123.456f;

    // 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);

    // binary formats
    std::cout << jdt::format("char: {0} = {0:b}\n", d);
    std::cout << jdt::format("short: {0} = {0:b}\n", e);
    std::cout << jdt::format("int: {0} = {0:b}\n", a);
    std::cout << jdt::format("double: {0} = {0:b}\n", b);
    std::cout << jdt::format("float: {0} = {0:b}\n", f);
    std::cout << jdt::format("std::string: {0} = {0:b}\n", c, c);
    std::cout << jdt::format("char*: {0} = {0:b}\n", "ABCDEFG");

    // 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);
}