Как опустить пустые аргументы в формате 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 * справа?», А не «как опустить пустые аргументы?» Или даже в более общем плане: как лучше всего отформатировать символ cn раз?

Barry 11.01.2023 01:40
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
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 (так что вы перераспределение сразу и заполнение, не полагаясь на модели амортизированного роста при перераспределении, чтобы спасти вас). Даже без предварительной reservestd::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);

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

Ваш один вкладыш создает строку, а затем копирует ее, плюс он довольно открыт для зависания (если вместо этого вы написали auto const& или auto&&).

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

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