0

I'm trying to add a filename prefix to each log string in spdlog. The Spdlog formatting string looks as this:

Test log {}

Logs are written as this: spdlog::error("Test log {}", value)

I'm trying to wrap this call and concatenate additional {} before formatting string, so I can pass the prefix of the file there.

static constexpr char * prefixHolder("{} ");

template<typename... Args>
void critical(const char fmt[], Args&&... args) const
{
    constexpr auto fullFmt = prefixHolder + fmt; //I can't find any solution for this

    spdlog::critical(fullFmt, m_prefix, std::forward<Args>(args)...);
}


Log log("MyClassOrLogger");

log.critical("My format {}", value);

Is it possible to solve this at compile time? I've tried some approaches, but I haven't found any way to make input fmt argument constexpr for the compiler.

C++ 17

Any suggestions or solutions?

Thanks

Ambrase
  • 57
  • 1
  • 10

2 Answers2

3

Parameter value cannot be used for constexpr.

You might turn:

template<typename... Args>
constexpr void critical(const char fmt[], Args&&... args)

into

template <char... cs, typename... Args>
void critical(std::integer_sequence<char, cs...>, Args&&... args)
{
    constexpr char fullFmt[] = {'{', '}', ' ', cs... , '\0'};

    spdlog::critical(fullFmt, m_prefix, std::forward<Args>(args)...);
}

to allow to have constexpr char array.

ensure-that-char-pointers-always-point-to-the-same-string-literal shows way to create similar sequences from literal string. In your case, it would be:

template <typename Char, Char... Cs>
constexpr auto operator"" _cs() -> std::integer_sequence<Char, Cs...> {
    return {};
}

Usage is then something like:

log.critical("My format {}"_cs, value); // gcc extension
log.critical(MAKE_SEQUENCE("My format {}"), value);

Demo with gcc extension.
Demo with MACRO.

Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • It converts _cs literal to const char[] by some reason `error: no matching function for call to 'Log::critical(const char [6])' logger.critical("Hello"_cs);` MAKE_STRING example is missing aligned_string. I found a similar implementation but with 2 template parameters in one of your replies, but it fails to compile for me still. The same issue as with _cs. gcc 7.3.1 @Jarod42 – Ambrase Sep 28 '20 at 18:44
  • 1
    Code in links need some adaptations, [Demo](https://godbolt.org/z/4v7Wro) with gcc extension. – Jarod42 Sep 29 '20 at 12:59
  • Can you help with the MAKE_STRING example? The portability of the extension variant concerns me. – Ambrase Sep 29 '20 at 14:59
  • 1
    [string-interning-at-compiletime](https://stackoverflow.com/questions/50288847/string-interning-at-compiletime-for-profiling/50289055#50289055) provides `MAKE_CHAR_SEQUENCE` which give a `template struct char_sequence` that you can use instead of `std::integer_sequence`. Else you can create traits to transform the former into the later. – Jarod42 Sep 29 '20 at 15:05
  • 1
    Portable demo added. – Jarod42 Sep 29 '20 at 15:12
  • Is there really no way or secret trick to write extra code that allows to drop the ugly `_cs` or `MAKE_SEQUENCE`? Can one use somehow implicit conversion from constexpr const char* to std::integer_sequence with correct deduced template parameters? And in C++23? – phinz May 01 '23 at 11:10
  • Even `consteval` doesn't allow to use parameter as `constexpr`. – Jarod42 May 01 '23 at 13:54
0

An old-time method using implicit string literal concatenation during compile time, often used in the case of multi-line strings:

#define HELLO_WORLD "Hello" " World" // -> Hello World

# define MULTI_LINE "Hello" \
    " World"                         // -> Hello World

You can use compile time log level macros of spdlog, might be:

#include <stdio.h>

#define SPDLOG_ERROR(...)  printf(__VA_ARGS__) // a dummy macro of spdlog

#define YOUR_prefixHolder "{} "
#define YOUR_ERROR1(fmt, ...) SPDLOG_ERROR(YOUR_prefixHolder fmt, ##__VA_ARGS__);
#define YOUR_ERROR2(...) SPDLOG_ERROR(YOUR_prefixHolder __VA_ARGS__);

int main()
{
    YOUR_ERROR1("I have %u apples\n", 5); // {} I have 5 apples
    YOUR_ERROR2("I have %u apples\n", 5); // {} I have 5 apples
    
    YOUR_ERROR1(5); // error!
    SPDLOG_ERROR(5) // 5
}
carnot
  • 71
  • 1
  • 3