0

I have a wrapper around fmt that prints to visual studio debug output. I tried to add wide string support:

template <typename Arg, typename... Args>
void fmt(Arg&& arg, Args&&... args)
{
    auto out = fmt::format(arg, args...);
    if constexpr (std::is_same<Arg, char8_t*>)
        OutputDebugStringA(out.c_str());
    else if constexpr (std::is_same<Arg, char16_t*>)
        OutputDebugStringW(out.c_str());
    else
        static_assert(false);
}

But it seems are a lot of different types you might want to match together

const char (&)[N], const char*, char* etc.

How do you handle this elegantly?

Tom Huntington
  • 2,260
  • 10
  • 20
  • 2
    Not sure, but maybe using [std::is_convertible](https://en.cppreference.com/w/cpp/types/is_convertible) instead of `std::is_same` can help ? – wohlstad Sep 01 '23 at 06:48
  • 1
    Instead of creating `OutputDebugStringA()` and `OutputDebugStringW()`, why not have a single `OutputDebugString()`? That pushes down another level - you'll either end up at a generic level of standard library (e.g. `basic_ostream`) or somewhere you can specialize the different versions. – Toby Speight Sep 01 '23 at 07:01
  • How about 2 overloads? one with `std::format_string` one with `std::format_wstring`? – Jarod42 Sep 01 '23 at 09:14

2 Answers2

1

if constexpr and static_assert don't work that well together (until C++23). This example is a C++17 version with SFINAE (for C++20 use concepts).

#include <iostream>
#include <string>
#include <string_view>
#include <type_traits>
#include <fmt/format.h>
#include <fmt/xchar.h>

// static_assert and `constexpr if` don't match, you will always get static_asset failure
// (up to C++20)

template<typename type_t, typename... args_t>
auto my_fmt(type_t&& fmt, args_t&&... args) -> std::enable_if_t<std::is_constructible_v<std::string_view, type_t>, std::string>
{
    // for runtime format strings use vformat and make_format_args
    auto output = fmt::vformat(std::string_view{ fmt }, fmt::make_format_args(args...));
    return output;
}

template<typename type_t, typename... args_t>
auto my_fmt(type_t&& fmt, args_t&&... args) -> std::enable_if_t<std::is_constructible_v<fmt::v10::wstring_view, type_t>, std::wstring>
{
    std::wstring output = fmt::vformat(fmt::v10::wstring_view{ fmt }, fmt::make_wformat_args(args...));
    return output;
}

int main()
{
    std::cout << " char : " << my_fmt("{}", 42) << "\n";
    std::wcout << " wchar_t : " << my_fmt(L"{}", 42) << "\n";

    return 0;
}
Pepijn Kramer
  • 9,356
  • 2
  • 8
  • 19
  • I've just been looking at how to do `if constexpr` `static_assert` in C++20 https://stackoverflow.com/a/64354296/11998382 How do you do it in C++23? https://godbolt.org/z/88rMbzjEo – Tom Huntington Sep 01 '23 at 07:37
  • Oh I just need to use the latest versions https://godbolt.org/z/8jEs93rPq – Tom Huntington Sep 01 '23 at 07:47
1
template <typename Arg, typename... Args>
void fmt(Arg&& arg, Args&&... args)
{
    if constexpr (std::is_convertible_v<Arg, const char*>)
        OutputDebugStringA(arg);
    else if constexpr (std::is_convertible_v<Arg, const wchar_t*>)
        OutputDebugStringW(arg);
    else 
        []<bool flag = false>(){
        static_assert(flag, "no match");}();
}

https://godbolt.org/z/c94nWq5vs

Tom Huntington
  • 2,260
  • 10
  • 20