Hello @ChuckieAJ ,
Since you mentioned needing to work with CString and eventually pulling format strings from the String Table resource, you might want to consider the following approach to see if it fits your project's architecture.
1. Extending std::format for CString
By default, std::format does not natively support MFC's CString. One potential workaround is to provide a custom std::formatter specialization. You could inherit from std::formatter<std::wstring_view, wchar_t>, which is often a relatively efficient way to parse and format the string without causing unnecessary string copies.
Documentation reference: std::formatter (cppreference)
#include <format>
#include <string_view>
// This could be placed in your pch.h or a common header
template <>
struct std::formatter<CString, wchar_t> : std::formatter<std::wstring_view, wchar_t>
{
template <class FormatContext>
auto format(const CString& str, FormatContext& ctx) const
{
return std::formatter<std::wstring_view, wchar_t>::format(
std::wstring_view(str.GetString(), str.GetLength()), ctx);
}
};
2. Template overloads in the header
To handle both hardcoded strings and Resource IDs, you could define a couple of template overloads directly in your CBaseCsvImporter.h. Using std::vformat inside the template might be a convenient way to format the arguments at runtime.
Documentation reference: std::vformat (cppreference)
class CBaseCsvImporter
{
public:
// 1. The base function (typically kept in your .cpp file)
void LogInfo(const CString& msg);
// 2. A suggested overload for hardcoded string literals
template<typename... Args>
void LogInfo(std::wstring_view fmt, Args&&... args)
{
try {
std::wstring msg = std::vformat(fmt, std::make_wformat_args(args...));
LogInfo(CString(msg.c_str()));
}
catch (const std::format_error& e) {
// std::vformat may throw if arguments mismatch
CString errMsg;
errMsg.Format(L"[Format Error]: %S", e.what());
LogInfo(errMsg);
}
}
// 3. A suggested overload for String Table Resource IDs
template<typename... Args>
void LogInfo(UINT nResourceID, Args&&... args)
{
CString resString;
if (resString.LoadString(nResourceID))
{
// Extract wstring_view and forward it
LogInfo(std::wstring_view(resString.GetString(), resString.GetLength()), std::forward<Args>(args)...);
}
else
{
LogInfo(CString(L"[ERROR: Missing Resource ID]"));
}
}
};
3. Example usage
If you decide to implement the above templates, you would typically be able to use your logging functions like this:
CString publisher = L"Example Corp";
int csvLine = 42;
// With a hardcoded string:
LogInfo(L"Skipped line {}: duplicate publisher '{}'", csvLine, publisher);
// With a String Table Resource ID:
LogInfo(IDS_LOG_SKIP_DUPLICATE, csvLine, publisher);
This setup might be a viable path to bridge modern C++20 features with traditional MFC types, while also preparing your application for localization. You can review this pattern to see if it aligns with your codebase.
Hope this gives you some useful ideas! If you found my response helpful or informative, I would greatly appreciate it if you could follow this guide for your confirmation.
Thank you.