Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / C++ Новый топик    Ответить
Топик располагается на нескольких страницах: Ctrl  назад   1 [2] 3   вперед  Ctrl      все
 Re: Вариативные шаблоны C++ и вариативные функции Си  [new]
Cerebrum
Member

Откуда: Омикрон Персей 8
Сообщений: 7937
petrav,

уже говорил и скажу еще раз, для тех кто в танке - юзай fold expression
моя идея представлена ниже (это только набросок, но он вполне пригоден для ее понимания и даже компилируется, проверил)
дальше доводить ее до ума придется тебе самому

#include <array>

template <size_t N>
using static_string = std::array<const char, N>;

template <size_t N, size_t ...Indexes>
constexpr static_string<N> make_static_string(const char (&str)[N], std::index_sequence<Indexes...>)
{
	return {str[Indexes] ..., '\0'};
}

template <size_t N, typename Indexes = std::make_index_sequence<N - 1>>
constexpr static_string<N> make_static_string(const char (&str)[N])
{
	return make_static_string(str, Indexes{});
}

template <typename T>
struct type_processor;

template <>
struct type_processor<float>
{
	template <size_t N>
	constexpr explicit type_processor(const static_string<N>& fmt, float fValue, char** ppOutBuff) : value(fValue), pp_out_buff(ppOutBuff)
	{
		// я не знаю (нет ни времени ни желания разбираться) как сделать проверку типа в форматной строке в compile time
		// после проверки форматная строка должна быть смещена на X позиций, чтобы следующая итерация проверяла следующий
		// тип в форматной строке

		static_assert(N >= 2 /*&& fmt[0] == '%' && fmt[1] == 'f'*/);
	}

	constexpr bool operator ()(void)
	{
		// TODO: конвертируем value в строку и записываем в pp_out_buff 
		
		// смещаем указатель pp_out_buff за блок записанных данных
		return true;
	}

	char**	pp_out_buff;
	float	value;
};

template <>
struct type_processor<int>
{
	template <size_t N>
	constexpr explicit type_processor(const static_string<N>& fmt, int nValue, char** ppOutBuff) :	value(nValue), pp_out_buff(ppOutBuff)
	{
		static_assert(N >= 2 /*&& fmt[0] == '%' && fmt[1] == 'f' && ppOutBuff != nullptr*/);
	}

	constexpr bool operator ()(void) 
	{
		// TODO: конвертируем value в строку и записываем в pp_out_buff
		
		// смещаем указатель pp_out_buff за блок записанных данных;
		return true;
	}

	char**	pp_out_buff;
	int		value;
};

template <>
struct type_processor<const char*>
{
	template <size_t N>
	constexpr explicit type_processor(const static_string<N>& fmt, const char* pValue, char** ppOutBuff) :	value(pValue), pp_out_buff(ppOutBuff)
	{
		static_assert(N >= 2 /*&& fmt[0] == '%' && fmt[1] == 'f' && ppOutBuff != nullptr*/);
	}

	constexpr bool operator ()(void) 
	{
		// TODO: копируем value в pp_out_buff 
		
		// смещаем указатель pp_out_buff за блок записанных данных
		return true;
	}

	char**		pp_out_buff;
	const char*	value;
};

template <size_t N, typename ...ArgsT>
constexpr bool my_printf(const static_string<N>& fmt, ArgsT ...vArgs)
{
	// TODO: организовать автоматический рассчет буфера под результирующую строку
	// 
	size_t szLength = 0;
	// (type_processor<ArgsT>(fmt, vArgs, (char**)&outBuff).calculate_size(szLength) && ...);

	char outBuff[256] = {0};
	return (type_processor<ArgsT>(fmt, vArgs, (char**)&outBuff)() && ...);
}

int main()
{
	constexpr auto fmt = make_static_string("%f %d %s");
	my_printf(fmt, 0.56f, 10, "some string");
	return 0;
}

если ты попробуешь передать в my_printf значение неизвестного type_processor типа, то на этапе компиляции уже получишь ор. Добавив специализаций type_processor можно покрыть все стандартные типы, которые должна жрать и преобразовывать к строке my_printf, таким образом, можно расширять набор ее типов по мере необходимости. Все что не входит в этот круг - идет лесом на этапе компиляции. Сможешь сделать static_assert проверку куска форматной строки на соответствие типу type_process - будешь молодец!

Одна из больших проблем (опять же, как я говорил ранее) - нерегулярность форматной строки. В моем примере все типы четко фиксируемые двумя символами %[symbol]. Как проанализировать форматную строку в компайл тайме с нерегулярной структурой я хз, но кто-то говорил выше, что это - ерунда, ну, тогда я уверен, все у вас получится

Вторая большая проблема - размер буфера под выходные данные. В своем примере его задал статически, но если получится все что написано выше, то размер можно посчитать таким же макаром через fold expression. Это можно сделать в тех же type_expression специализациях, добавив в них функции calculate_size или переопределив оператор ().

Сначала прогоняешь по форматной строке и типам с целью определения размера необходимого буфера, выделяешь его, потом выполняешь окончательную обработку с записью полученного результата, как показано у меня в примере.

может это https://habr.com/ru/post/428846/
еще поможет или натолкнет на какие-то мысли по анализу форматной строки в compile time

PS. я не стал заморачиваться с std::decay_t, но тебе придется

Сообщение было отредактировано: 1 май 20, 13:25
1 май 20, 13:26    [22126179]     Ответить | Цитировать Сообщить модератору
 Re: Вариативные шаблоны C++ и вариативные функции Си  [new]
Anatoly Moskovsky
Member

Откуда: Odessa
Сообщений: 6491
Cerebrum,

return (type_processor<ArgsT>(fmt, vArgs, (char**)&outBuff)() && ...);

Ну и где здесь fold-expression над типом? )))
1 май 20, 13:45    [22126190]     Ответить | Цитировать Сообщить модератору
 Re: Вариативные шаблоны C++ и вариативные функции Си  [new]
Anatoly Moskovsky
Member

Откуда: Odessa
Сообщений: 6491
petrav
Господа, нам осталось решить одну проблему: как в одном вызове совместить и статическую проверку данных и динамическую печать данных? Желательно найти выход без макросов. Неужели выхода нет?! :-((( Вот псевдокод, он не компилируется:


Чтобы применить static_assert к выражению, все его составляющие должны быть известны на этапе компиляции.
В частности это означает что строка формата не может быть аргументом функции, а должна быть передана как аргумент шаблона.
Поэтому без макроса нельзя обойтись если вы хотите чтобы для юзера все выглядело как обычная функция.

Сообщение было отредактировано: 1 май 20, 13:56
1 май 20, 13:57    [22126196]     Ответить | Цитировать Сообщить модератору
 Re: Вариативные шаблоны C++ и вариативные функции Си  [new]
Cerebrum
Member

Откуда: Омикрон Персей 8
Сообщений: 7937
Anatoly Moskovsky
Ну и где здесь fold-expression над типом? )))

в type_processor'e
специализация выбирается на основе типа, нет специализации - нет компиляции.

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

Сообщение было отредактировано: 1 май 20, 14:17
1 май 20, 14:16    [22126211]     Ответить | Цитировать Сообщить модератору
 Re: Вариативные шаблоны C++ и вариативные функции Си  [new]
Anatoly Moskovsky
Member

Откуда: Odessa
Сообщений: 6491
Cerebrum
ясен пень, что fold expression не сделает цепочку типов, это и не подразумевалось,
странно, что приходится это объяснять, в особенности после того, как я привел аж два примера иллюстрирующих мое высказывание

Начнем с того что fold было приведено как замена рекурсивной обработки списка типов в компайл-тайм.
При этом все примеры работают не с типами и не в компайл-тайм.
Если мы вместо типов работаем со значениями, то все что тут делает fold можно сделать и обычным циклом )))
1 май 20, 14:32    [22126219]     Ответить | Цитировать Сообщить модератору
 Re: Вариативные шаблоны C++ и вариативные функции Си  [new]
petrav
Member

Откуда:
Сообщений: 2075
Cerebrum
petrav,

уже говорил и скажу еще раз, для тех кто в танке - юзай fold expression
моя идея представлена ниже (это только набросок, но он вполне пригоден для ее понимания и даже компилируется, проверил)
дальше доводить ее до ума придется тебе самому

Спасибо за код. :) На уровне концепции я его понял. Но он не будет работать в compile time. И Вы делаете немного не то что Вас просят. :)

Строку формата в стиле "%21.567f" я уже разбирал compile-time выше в этой ветке. Только что я написал рекурсивный обход по типам тоже compile time. Осталось совместить.

И погуглить как строку формата сделать аргументом шаблона.
1 май 20, 16:40    [22126282]     Ответить | Цитировать Сообщить модератору
 Re: Вариативные шаблоны C++ и вариативные функции Си  [new]
petrav
Member

Откуда:
Сообщений: 2075
Cerebrum

в type_processor'e
специализация выбирается на основе типа, нет специализации - нет компиляции.

Кстати! В Вашем коде есть очень большой изъян. Нет специализации, но есть приведение типов! Именно поэтому у вас получилось написать специализацию для const char *, хотя в Вашем примере аргумент "some string" -- это не const char * ! Это массив. Вот Ваш тестовый код:

my_printf(fmt, 0.56f, 10, "some string");


Т.е. Вы с таким подходом можете написать специализацию для double, но забыть специализацию для char. И Ваш код, при передаче char, будет компилироваться, но не будет проверять соответствие типов аргументов и строки формата. Даже в run time! Мне так кажется.

Т.е. правильно говорит Anatoly Moskovsky, Вы работаете не с типами. Вы работаете со значениями.

Вот мне пришлось помучится что бы написать специализацию для аргумента (не формата) в стиле "some string". Потому что эти рекурсивные шаблоны чётко руководствуются типом, а не значением.
1 май 20, 19:17    [22126341]     Ответить | Цитировать Сообщить модератору
 Re: Вариативные шаблоны C++ и вариативные функции Си  [new]
Cerebrum
Member

Откуда: Омикрон Персей 8
Сообщений: 7937
petrav
Вот мне пришлось помучится что бы написать специализацию для аргумента (не формата) в стиле "some string"

Cerebrum
PS. я не стал заморачиваться с std::decay_t, но тебе придется

откуда вы только беретесь такие со своим Московским...

Модератор: Вложение удалено.


Модератор: Вложение удалено.


Сообщение было отредактировано: 2 май 20, 09:01
1 май 20, 19:34    [22126348]     Ответить | Цитировать Сообщить модератору
 Re: Вариативные шаблоны C++ и вариативные функции Си  [new]
petrav
Member

Откуда:
Сообщений: 2075
Cerebrum
petrav
Вот мне пришлось помучится что бы написать специализацию для аргумента (не формата) в стиле "some string"

Cerebrum
PS. я не стал заморачиваться с std::decay_t, но тебе придется

откуда вы только беретесь такие со своим Московским...

Так я и не утверждаю, что разбираюсь в С++17. Но не очень понятно, что будет в Вашем примере, если мы определим перегрузку (явную специализацию шаблона) только для double, подставим %12f в виде формата, подставим char в runtime как аргумент, а потом всё это передадим в snprintf(). Передача в snprintf() это требование.

И причём тут std::decay...
1 май 20, 20:23    [22126371]     Ответить | Цитировать Сообщить модератору
 Re: Вариативные шаблоны C++ и вариативные функции Си  [new]
petrav
Member

Откуда:
Сообщений: 2075
Cerebrum
petrav
Вот мне пришлось помучится что бы написать специализацию для аргумента (не формата) в стиле "some string"

Cerebrum
PS. я не стал заморачиваться с std::decay_t, но тебе придется

откуда вы только беретесь такие со своим Московским...

Мне кажется, Вы задействовали механизм преобразования типов аргументов. А по ТЗ нужно однозначно и чётко проверить соответствие типов аргументов и строки формата. В compile time. Ну или хотя бы в run time, но однозначно.
1 май 20, 20:52    [22126378]     Ответить | Цитировать Сообщить модератору
 Re: Вариативные шаблоны C++ и вариативные функции Си  [new]
Anatoly Moskovsky
Member

Откуда: Odessa
Сообщений: 6491
petrav
И Вы делаете немного не то что Вас просят. :)

Ну так всегда бывает, когда узнал новую фичу, и теперь суешь ее везде без разбору )))
1 май 20, 20:53    [22126379]     Ответить | Цитировать Сообщить модератору
 Re: Вариативные шаблоны C++ и вариативные функции Си  [new]
Anatoly Moskovsky
Member

Откуда: Odessa
Сообщений: 6491
petrav
И причём тут std::decay...

std::decay автоматически выведет тип указателя из типа строкового литерала (без явного указания типов)

Сообщение было отредактировано: 1 май 20, 20:58
1 май 20, 20:55    [22126380]     Ответить | Цитировать Сообщить модератору
 Re: Вариативные шаблоны C++ и вариативные функции Си  [new]
petrav
Member

Откуда:
Сообщений: 2075
Anatoly Moskovsky
petrav
И причём тут std::decay...

std::decay автоматически выведет тип указателя из типа строкового литерала (без явного указания типов)

Да, я это понял... со второго раза, правда. Но лучше поздно, чем... Я думаю "std::decay" тут ненужен. Он сносит дополнительную информацию с типов, а это ни к чему, мы всё проверим compile time. Я тут проще и надёжнее вещь спрограммировал. И Вы знаете, все "static_assert" работают, достаточно инвертировать условие -- и тут же код перестаёт собираться. Если аргумент "12.8" переделать на "12.8F", то код тоже перестаёт собираться. Т.е. корректность максимальная.

Вот тестовый код:

void newTest()
{
    double d = std::rand();
    char* s = "Jim";

    bool const res1 =
        print_f("Angle: %0.3f. Mode: %s. Name: %s.", d, "Prepare", s);
    bool const res2 =
        print_f("My float: %f.", 12.8);
    bool const res3 =
        print_f("Hello, world !");
}


А вот код обхода типов compile time, основанный на Вашем примере.

template <size_t N>
constexpr void processArray(char const (&)[N])
{
    static_assert(N > 0); // Я знаю, что массив не может быть размера ноль.
}

template <typename T>
constexpr void processType()
{
    // Для аргументов типов "строковый литерал".
    static_assert(std::is_array_v<T>);
    // А вот тут уже значения, а не типы, но приведение типов не работает с
    // массивами. Ну, наверное, не работает. Скорее всего…
    processArray(T{});
}

template <>
constexpr void processType<char *>()
{
}

template <>
constexpr void processType<double>()
{
}

template <size_t N>
constexpr void validateFormatArgs()
{
    static_assert(N == 0);
}

template <size_t N, typename T, typename... Rest>
constexpr void validateFormatArgs()
{
    processType<T>();
    validateFormatArgs<N-1, Rest...>();
}

template <size_t Size, typename... Parameters>
constexpr bool checkArgs(char const (&format)[Size],
                         Parameters const &... parameters)
{
    // Compile time навигация по типам.
    validateFormatArgs<sizeof...(Parameters), Parameters...>();
}
2 май 20, 19:33    [22126748]     Ответить | Цитировать Сообщить модератору
 Re: Вариативные шаблоны C++ и вариативные функции Си  [new]
a guest
Member

Откуда:
Сообщений: 255
petrav
Если аргумент "12.8" переделать на "12.8F", то код тоже перестаёт собираться. Т.е. корректность максимальная.
В каком смысле «максимальная корректность», если
printf("My float: %f.", 12.8f);
это ОК?
3 май 20, 21:53    [22127008]     Ответить | Цитировать Сообщить модератору
 Re: Вариативные шаблоны C++ и вариативные функции Си  [new]
petrav
Member

Откуда:
Сообщений: 2075
a guest
petrav
Если аргумент "12.8" переделать на "12.8F", то код тоже перестаёт собираться. Т.е. корректность максимальная.
В каком смысле «максимальная корректность», если
printf("My float: %f.", 12.8f);
это ОК?

В каком смысле ОК? В плане стандарта C++, да ОК. В плане однозначной проверки типов -- супер ОК. Не написана специализация для "float" и выключено приведение типов. Компиляция ломается. Это Супер ОК. Только что написал специализацию для "float" -- тут же начало компилироваться.

Или Вы про различие "%f", "%lf" и "%Lf" ?
3 май 20, 22:29    [22127018]     Ответить | Цитировать Сообщить модератору
 Re: Вариативные шаблоны C++ и вариативные функции Си  [new]
a guest
Member

Откуда:
Сообщений: 255
petrav
a guest
пропущено...
В каком смысле «максимальная корректность», если
printf("My float: %f.", 12.8f);
это ОК?

В каком смысле ОК?
В том смысле, что это корректное использование printf.
Просто странно читать про «максимальную корректность» чекера аргументов, который должен разрешать всё то, что допустимо для обычного printf, а он отказывается компилировать.
3 май 20, 23:45    [22127032]     Ответить | Цитировать Сообщить модератору
 Re: Вариативные шаблоны C++ и вариативные функции Си  [new]
petrav
Member

Откуда:
Сообщений: 2075
a guest
petrav
пропущено...

В каком смысле ОК?
В том смысле, что это корректное использование printf.

Под «максимальной корректностью» имелась в виду возможность однозначно в compile time детектировать тип аргумента. И запретить приведение типов. Т.е. под корректностью имелась возможность чётко увидеть тип. И никаких "std::decay".

a guest
Просто странно читать про «максимальную корректность» чекера аргументов, который должен разрешать всё то, что допустимо для обычного printf, а он отказывается компилировать.

Нет… Чекер, наоборот должен запрещать всякие бессмысленные изыски. Зачем "%d", если есть "%i" ? На сайте cppreference написано, что "%f" и "%lf" эквивалентны. А `%Lf` -- это "long double", которая с 2005-го года не 80-т бит, а соответствует "double". Кстати, зачем нам аргументы типа "float" ? Да по-запрещать всё к чертям. Я такие бардаки годами разбираю. И везде какие-то глюки и косяки.

Нам нужно сузить формат, а потом расширить его на печать, например, углов. С авто. преобразованием радиан в углы. И это тоже уменьшит количество косяков.
4 май 20, 00:38    [22127049]     Ответить | Цитировать Сообщить модератору
 Re: Вариативные шаблоны C++ и вариативные функции Си  [new]
Dimitry Sibiryakov
Member

Откуда:
Сообщений: 49957

А может, всё-таки проще компилятор сменить? Что вы так к убогой студии привязались?..

Posted via ActualForum NNTP Server 1.5

4 май 20, 00:53    [22127050]     Ответить | Цитировать Сообщить модератору
 Re: Вариативные шаблоны C++ и вариативные функции Си  [new]
Anatoly Moskovsky
Member

Откуда: Odessa
Сообщений: 6491
Dimitry Sibiryakov
А может, всё-таки проще компилятор сменить? Что вы так к убогой студии привязались?..

Я так понял что помимо printf надо будет реализовать нестандартные форматы, и смена компилятора тут не поможет.
4 май 20, 01:02    [22127051]     Ответить | Цитировать Сообщить модератору
 Re: Вариативные шаблоны C++ и вариативные функции Си  [new]
mayton
Member

Откуда: loopback
Сообщений: 46320
Да все претензии по форматной печати можно адресовать к конкретной библиотеке. Вот ее и ругайте.

Компиллятор-то тут причем?
4 май 20, 10:12    [22127101]     Ответить | Цитировать Сообщить модератору
 Re: Вариативные шаблоны C++ и вариативные функции Си  [new]
petrav
Member

Откуда:
Сообщений: 2075
Dimitry Sibiryakov

А может, всё-таки проще компилятор сменить? Что вы так к убогой студии привязались?..

Студия прекрасна. Я почитал документацию, да хорошая штука:

void my_printf(int x, int y, char const *format, ...)
    __attribute__((format(printf, 3, 4)));

void my_printf(int x, int y, char const *format, ...)
{
}


В Студии есть аналогичный атрибут: _Printf_format_string_. И там ещё куча атрибутов, другое дело что активация этого анализатора кода у меня сборку проекта замедляет в шесть раз. И начинают лезть предупреждения на сторонние библиотеки. Не знаю что делать с этим.
4 май 20, 11:22    [22127143]     Ответить | Цитировать Сообщить модератору
 Re: Вариативные шаблоны C++ и вариативные функции Си  [new]
petrav
Member

Откуда:
Сообщений: 2075
Anatoly Moskovsky
Dimitry Sibiryakov
А может, всё-таки проще компилятор сменить? Что вы так к убогой студии привязались?..

Я так понял что помимо printf надо будет реализовать нестандартные форматы, и смена компилятора тут не поможет.

Да... вот только...

// Можно было бы написать так с преобразованием радиан в углы.
print_f("%ang", 0.3);
// Но зачем это делать если можно написать так?
std::printf("%f", toAng(0.3));
// В обоих случаях нужно явно указать, что в данных радианы.

Собственная compile time проверка формата с преобразованием значений в run time имела бы смысл в таких случаях:

Angle<double, AngType::Radian> ang = {};
double angles[3] = {};
std::string str;
print_f("%ang %ang_arr %s", ang, angles, str);

Смутные сомнения об overengineering меня, конечно, терзали с самого начала...

Сообщение было отредактировано: 4 май 20, 12:29
4 май 20, 12:29    [22127179]     Ответить | Цитировать Сообщить модератору
 Re: Вариативные шаблоны C++ и вариативные функции Си  [new]
Cerebrum
Member

Откуда: Омикрон Персей 8
Сообщений: 7937
petrav
Смутные сомнения об overengineering меня, конечно, терзали с самого начала...

на мой взгляд для type rich кода следует использовать operator "" с собственными суффиксами для типов и перегрузку operator <<, а не измываться над printf, который уже много раз предавали анафеме
4 май 20, 12:49    [22127188]     Ответить | Цитировать Сообщить модератору
 Re: Вариативные шаблоны C++ и вариативные функции Си  [new]
Cerebrum
Member

Откуда: Омикрон Персей 8
Сообщений: 7937
Cerebrum
petrav
Смутные сомнения об overengineering меня, конечно, терзали с самого начала...

на мой взгляд для type rich кода следует использовать operator "" с собственными суффиксами для типов и перегрузку operator <<, а не измываться над printf, который уже много раз предавали анафеме
тогда никаких проблем с разделением углов в радианах от углов в градусах не будет
4 май 20, 12:50    [22127189]     Ответить | Цитировать Сообщить модератору
 Re: Вариативные шаблоны C++ и вариативные функции Си  [new]
petrav
Member

Откуда:
Сообщений: 2075
Cerebrum
petrav
Смутные сомнения об overengineering меня, конечно, терзали с самого начала...

на мой взгляд для type rich кода следует использовать operator "" с собственными суффиксами для типов и перегрузку operator <<, а не измываться над printf, который уже много раз предавали анафеме

Вы имеете в виду операторы в стиле "123.4_rad" ? Да, штука хорошая, но это уже про написание литералов, а не про печать данных.

Вообще, анафеме нужно предать библиотеку <iostream>. Совершено непонятно о чём думали люди которые это проектировали. Ведь были же все языковые инструменты, что бы разработать type safe форматирование строк в стиле "printf()" -- как это сделали в "QString" и "boost::format", например.

Впрочем, C++ это язык для создания велосипедов и костылей. Может они неосознанно так проектировали <iostream>, что бы пользователей спровоцировать к творчеству через шок. Кстати, Страуструп, собственно, и писал, что C++ проектируется так, что бы пользователи могли расширять язык своими типами. Видимо многоуважаемый Бьёрн передал эту мысль другим проектантам и они накосячили не осознанно.

А может и осознанно...
4 май 20, 16:48    [22127281]     Ответить | Цитировать Сообщить модератору
Топик располагается на нескольких страницах: Ctrl  назад   1 [2] 3   вперед  Ctrl      все
Все форумы / C++ Ответить