Re[19]: Cоздание базового шаблона минуя специализацию
От: so5team https://stiffstream.com
Дата: 29.10.22 06:43
Оценка: 20 (3)
Здравствуйте, rg45, Вы писали:

R>Так о том и речь, что их нет, а вместо функций — шаблон класса std::formatter. А если бы std::format для кастомизации использовал бы не шаблон класса со специализациями, а неквалифицированный вызов функции с каким-то предопределенным именем, это могло бы дать ряд преимуществ:


Могу ошибаться, но может быть есть способ объединить лучшее из двух миров?

Идея в том, что сейчас fmtlib просто инстанциирует тип formatter там, где ей это нужно. Наверное, можно создавать formatter не напрямую, а вызвав функцию-фабрику, которая уже и возвратит экземпляр formatter.

Функция-фабрика будет свободной функцией, которая может быть определена и в пространстве имен пользователя.

Небольшой набросок слепленный на коленке для того, чтобы продемонстрировать идею:
  откровенный колхозинг
#include <algorithm>
#include <iostream>
#include <string>
#include <string_view>
#include <type_traits>
#include <sstream>

namespace fmtlib
{

template<typename T, typename CharT = char>
struct formatter;

template<typename CharT, typename T>
[[nodiscard]]
formatter<T, CharT>
make_formatter(const T &);

template<typename CharT, typename T>
CharT *
format(CharT * dest, T && value)
    {
        return make_formatter<CharT>(value).format(dest, value);
    }

template<>
struct formatter<std::string_view, char>
    {
        char *
        format(char * dest, const std::string_view & v)
            {
                *(dest++) = '"';
                std::copy(v.begin(), v.end(), dest);
                dest += v.size();
                *(dest++) = '"';
                return dest;
            }
    };

template<>
struct formatter<std::string, char>
    :    public formatter<std::string_view, char>
    {
        char *
        format(char * dest, const std::string & v)
            {
                return formatter<std::string_view, char>::format(
                        dest, std::string_view{v});
            }
    };

template<>
struct formatter<char, char>
    {
        char *
        format(char * dest, const char v)
            {
                *(dest++) = v;
                return dest;
            }
    };

template<typename T>
struct formatter<T, std::enable_if_t<std::is_arithmetic_v<T>, char>>
    {
        char *
        format(char * dest, const T & v)
            {
                std::ostringstream ss;
                ss << v;
                const auto sv = ss.str();
                std::copy(sv.begin(), sv.end(), dest);
                dest += sv.size();
                return dest;
            }
    };

template<typename CharT, typename T>
[[nodiscard]]
formatter<T, CharT>
make_formatter(const T &)
    {
        return {};
    }

} /* namespace fmtlib */

namespace one
{

template<typename T>
struct point
    {
        T x;
        T y;
    };

} /* namespace one */

template<typename T>
struct fmtlib::formatter<one::point<T>, char>
    {
        char *
        format(char * dest, const one::point<T> & v)
        {
            dest = fmtlib::format(dest, '{');
            dest = fmtlib::format(dest, v.x);
            dest = fmtlib::format(dest, ',');
            dest = fmtlib::format(dest, v.y);
            dest = fmtlib::format(dest, '}');
            return dest;
        }
    };

namespace two
{

template<typename T, typename Tag>
struct value_holder
    {
        T v;
    };

template<typename T, typename Tag, typename CharT>
struct value_holder_formatter
    {
        CharT *
        format(CharT * dest, const value_holder<T, Tag> & v)
        {
            return fmtlib::formatter<T, CharT>{}.format(dest, v.v);
        }
    };

template<typename CharT, typename T, typename Tag>
value_holder_formatter<T, Tag, CharT>
make_formatter(const value_holder<T, Tag> &)
    {
        return {};
    }

} /* namespace two */

int
main()
    {
        using namespace std::string_literals;
        using namespace std::string_view_literals;

        char buf[1024];

        struct tag_1 {};
        struct tag_2 {};

        char * p = fmtlib::format(buf, 42);
        p = fmtlib::format(p, ' ');
        p = fmtlib::format(p, "is the answer"s);
        p = fmtlib::format(p, ';' );
        p = fmtlib::format(p, one::point<int>{3,4});
        p = fmtlib::format(p, '-' );
        p = fmtlib::format(p, one::point<float>{0.1,-1.2});
        p = fmtlib::format(p, ';' );
        p = fmtlib::format(p, two::value_holder<short, tag_1>{333});
        p = fmtlib::format(p, '-' );
        p = fmtlib::format(p, two::value_holder<unsigned long long, tag_2>{555});
        *p = 0;

        std::cout << "Result: " << buf << std::endl;
    }

Проверялось на g++-11 в режиме C++17


Не знаю насколько реально все это впилить в fmtlib, т.к. там тип formatter нужен для вложенного типа basic_format_context::formatter_type. Но если реально, то можно расширить функциональность fmtlib сохранив при этом совместимость с уже имеющимся кодом, в котором делаются специализации fmt::formatter. И, соответственно, подобный же трюк можно будет проделать и с std::format.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.