Как опустить пустые аргументы в формате fmt::format?

Есть ли способ опустить пустые строковые литералы ("") в списке аргументов функции fmt::format?

У меня есть приведенный ниже фрагмент, который дает желаемый результат:

#include <string>
#include <fmt/core.h>


int main( )
{
    const std::string application_layer_text_head { fmt::format( "{:*<5}[Application Layer]{:*<51}\n\n", "", "" ) };
    fmt::print( "{}", application_layer_text_head );
}

Выход:

*****[Application Layer]***************************************************

Итак, вместо того, чтобы писать это: fmt::format( "{:*<5}[Application Layer]{:*<51}\n\n", "", "" ), можем ли мы удалить пустые литералы и написать это: fmt::format( "{:*<5}[Application Layer]{:*<51}\n\n" )? Я пробовал, но не удалось скомпилировать. Эти два литерала на самом деле не служат никакой цели, поэтому я хочу найти способ не писать их.

Просто чтобы уточнить, я хочу иметь только 5 * в начале, затем [Application Layer], затем 51 *, а затем 2 \n.

@ Никол Болас Я хочу не только 5 *. Посмотрите на выводимый текст. Это то, что я хочу. И fmt::format хорошо работает в этом случае и намного эффективнее, чем operator+ из std::string. Так что есть смысл его использовать.

digito_evo 11.01.2023 01:22

Это более эффективно, чем operator+, но operator+= и append позволяют избежать большинства этих затрат (перераспределение на месте, а не создание новых string и отбрасывание старых, и reserve можно использовать для обеспечения единого распределения, если вам это действительно нужно).

ShadowRanger 11.01.2023 01:33

Я думаю, что вопрос здесь действительно заключается в том, «Как мне отформатировать эту строку с 5 * слева и 51 * справа?», А не «как опустить пустые аргументы?» Или даже в более общем плане: как лучше всего отформатировать символ c n раз?

Barry 11.01.2023 01:40
Ускорьте разработку веб-приложений Laravel с помощью этих бесплатных стартовых наборов
Ускорьте разработку веб-приложений Laravel с помощью этих бесплатных стартовых наборов
Laravel - это мощный PHP-фреймворк, используемый для создания масштабируемых и надежных веб-приложений. Одним из преимуществ Laravel является его...
Что такое двойные вопросительные знаки (??) в JavaScript?
Что такое двойные вопросительные знаки (??) в JavaScript?
Как безопасно обрабатывать неопределенные и нулевые значения в коде с помощью Nullish Coalescing
Создание ресурсов API Laravel: Советы по производительности и масштабируемости
Создание ресурсов API Laravel: Советы по производительности и масштабируемости
Создание API-ресурса Laravel может быть непростой задачей. Она требует глубокого понимания возможностей Laravel и лучших практик, чтобы обеспечить...
Как сделать компонент справочного центра с помощью TailwindCSS
Как сделать компонент справочного центра с помощью TailwindCSS
Справочный центр - это веб-сайт, где клиенты могут найти ответы на свои вопросы и решения своих проблем. Созданный для решения многих распространенных...
Асинхронная передача данных с помощью sendBeacon в JavaScript
Асинхронная передача данных с помощью sendBeacon в JavaScript
В современных веб-приложениях отправка данных из JavaScript на стороне клиента на сервер является распространенной задачей. Одним из популярных...
Как подобрать выигрышные акции с помощью анализа и визуализации на Python
Как подобрать выигрышные акции с помощью анализа и визуализации на Python
Отказ от ответственности: Эта статья предназначена только для демонстрации и не должна использоваться в качестве инвестиционного совета.
0
3
94
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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

Так что нет, у format нет механизма, который позволил бы вам избежать предоставления данных, предоставленных пользователями, которые и являются основной причиной существования format.

Также следует отметить, что само значение текста после : в спецификаторе формата определяется исходя из типа форматируемого объекта. «*<5» означает «выровнять по 5 символам, используя символы «*» для заполнения пробелов» только потому, что вы предоставили строку для этого конкретного параметра формата. Так что format не только не дает возможности сделать это, но и функционально не может. Вы должны указать, какой тип используется, потому что это неотъемлемая часть обработки значения "*<5".

Так есть ли другой лучший (более чистый) способ сделать это? Я выбрал формат, потому что он эффективен.

digito_evo 11.01.2023 01:32

@digito_evo, вы можете создать собственный тип и написать для него собственные правила форматирования.

ALX23z 11.01.2023 03:54
Ответ принят как подходящий

Как уже отмечалось, format не может этого сделать. Но ваши опасения по поводу дороговизны конкатенации строк неуместны; повторное применение operator+ дорого (выполняет новые распределения, копирует все существующие данные и новые данные, отбрасывает старые данные снова и снова), но конкатенация на месте с operator+= и append дешева, особенно если вы предварительно reserve (так что вы перераспределение сразу и заполнение, не полагаясь на модели амортизированного роста при перераспределении, чтобы спасти вас). Даже без предварительной reserve std::string следует амортизированным моделям роста, поэтому повторная конкатенация на месте амортизируется O(1) (на каждый добавленный символ), а не O(n) в размере данных до сих пор.

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

#include <string>
#include <string_view>
#include <fmt/core.h>

using namespace std::literals;

int main( )
{
    // Make empty string, and reserve enough space for final form
    auto application_layer_text_head = std::string();
    application_layer_text_head.reserve(5 + "[Application Layer]"sv.size() + 51 + "\n\n"sv.size());

    // append is in-place, returning reference to original string, so it can be chained
    // Using string_view literals to avoid need for any temporary runtime allocated strings,
    // while still allowing append to use known length concatenation to save scanning for NUL
    application_layer_text_head.append(5, '*').append("[Application Layer]"sv).append(51, '*').append("\n\n"sv);
    fmt::print("{}", application_layer_text_head);
}

Если вас устраивали некоторые конкатенации, потенциально выполняющие перераспределение, и конструкция окончательного перемещения для перемещения ресурсов из временной ссылки в реальную строку, это упрощается до однострочного:

const auto application_layer_text_head = std::move(std::string(5, '*').append("[Application Layer]"sv).append(51, '*').append("\n\n"sv));

Или, учитывая, что 5 звездочек достаточно для ввода, еще более короткая/простая версия:

const auto application_layer_text_head = std::move("*****[Application Layer]"s.append(51, '*').append("\n\n"sv));

Но сохранение его двухстрочным позволяет избежать конструкции перемещения и немного безопаснее:

auto application_layer_text_head = "*****[Application Layer]"s;
application_layer_text_head.append(51, '*').append("\n\n"sv);

Да, ни один из них не так красив, как литерал одного формата, даже с «уродливыми» пустыми заполнителями. Если вы предпочитаете внешний вид строки формата, просто передайте пустые заполнители так, как вы это уже делаете.

Barry 11.01.2023 01:54

@Barry: Хм ... Вероятно, вы правы, поскольку он возвращается по ссылке, а не по значению; Я предполагаю, что std::move можно использовать, по крайней мере, для того, чтобы сделать его конструкцией перемещения, и хотя я не могу представить, почему вы объявляете его ссылкой напрямую, это будет проблемой, если вы попытаетесь встроить его в вызов метода, который его получил. по (висящей) ссылке. Так что да, наверное, лучше всего в две строки.

ShadowRanger 11.01.2023 03:02

Спасибо за предложение. И если говорить о висячих ссылках, это действительно происходит в const auto application_layer_text_head = std::string(5, '*').append("[Application Layer]"sv).? Разве он не собирается делать конструкцию перемещения, потому что объект с правой стороны является временным (rvalue)?

digito_evo 11.01.2023 09:39

@digito_evo Правая часть - это не rvalue, это lvalue.

Barry 11.01.2023 14:32

@Барри Но почему? Это значение без какого-либо идентификатора. Как это может быть lvalue?

digito_evo 11.01.2023 14:57

@digito_evo Потому что идентификаторы - это не единственные вещи, которые являются lvalue. string::append() возвращает ссылку lvalue.

Barry 11.01.2023 16:38

@digito_evo: Причина, по которой он ведет себя таким образом, заключается в том, что он изменяет str на месте, чтобы избежать копирования, а возвращаемое значение всегда является ссылкой l-значения на исходный str. Это позволяет связывать вызовы методов без накладных расходов и не требует переназначения, когда у объекта уже есть имя, но это означает, что даже когда вы изначально работаете с r-значением, он все равно возвращает ссылку на l-значение. Они могли бы добавить версии append с указанием r-значения, но они не сделали этого с тех пор, как эта функция была добавлена ​​​​в C++ 11. Такова жизнь.

ShadowRanger 11.01.2023 18:23

Другие вопросы по теме