Click here to Skip to main content
15,887,746 members
Please Sign up or sign in to vote.
5.00/5 (1 vote)
See more:
I am trying to use std::format in combination with template parameter expansion.

The errors I am getting are:

1>src.cpp(22,21): error C7595: 'std::basic_format_string<char,std::string &,int &,int &>::basic_format_string': call to immediate function is not a constant expression
1>C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Tools\MSVC\14.35.32215\include\format(3373,62): message : failure was caused by a read of a variable outside its lifetime
1>C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Tools\MSVC\14.35.32215\include\format(3373,62): message : see usage of 'format'
1>src.cpp(34,44): message : see reference to function template instantiation 'void WriteLogFile<std::string,int,int>(const std::string &,const char *,int,std::string_view,std::string,int,int)' being compiled


What I have tried:

#include <chrono>
#include <fstream>
#include <format>
#include <iostream>

void WriteLogFile(const std::string& filename, const char* file, int line, std::string_view format, auto... args)
{
    std::ofstream logFile(filename, std::ios_base::app);
    if (!logFile)
    {
        std::cerr << "Failed to open log file." << std::endl;
        return;
    }

    auto now = std::chrono::system_clock::now();
    std::time_t currentTime = std::chrono::system_clock::to_time_t(now);

    std::string message = std::format("{} [{}:{}] - {}",
        std::ctime(¤tTime),
        file,
        line,
        std::format(format, args...));

    logFile << message << std::endl;
    std::cout << message << std::endl;

    logFile.close();
}

void BrewCoffee(const std::string& coffeeType, int milkAmount, int temperature)
{
    WriteLogFile("coffeeMachine.log", __FILE__, __LINE__,
        "Brewing a cup of {} with {}ml of milk at {} degrees.",
        coffeeType, milkAmount, temperature);
}

int main()
{
    BrewCoffee("Espresso", 0, 90);
    BrewCoffee("Cappuccino", 150, 85);

    return 0;
}
Posted
Updated 20-May-23 6:59am
Comments
Member 15627495 20-May-23 6:41am    
numberise your
 format("{0} {1} {2}{3}",to_str(var_0),var_1,var_2,var_3) ;


format("{3} {1} {0}{2}",to_str(var_0),var_1,var_2,var_3) ; // works too.

<pre>

It's Strings Interpolation
Michael Haephrati 20-May-23 7:28am    
I don't know the number of arguments.
The problem I have is with this code:

std::string message = std::format("{} [{}:{}] - {}",
std::ctime(¤tTime),
file,
line,
std::format(format, args...));

How do you suggest to fix it?
Member 15627495 20-May-23 7:36am    
you have to use an overload on few self mades functions, to prepare both the 'mask' / and an array for the values...

one important thing about the mask ( left value for the format function ),
it's a string too , and you can 'prepare' a reliable 'mask' too ( for each case ).
It's a string build before you use the Format function.

As you don't have a fixed number of arguments, it's the best way.

The error message suggests that the issue is with the last line in the call to format.
C++
std::format(format, args...)

Try moving that call outside and capturing the resultant string, which you can than pass as the final parameter.

TBH I find this function less than easy to understand, and have only used it in quite simple situations.

[edit]
Having read further, I suspect the actual issue is that the format parameter being passed in, is the actual problem, as it is not a constant. Take a look at std::basic_string_view - cppreference.com[^] for details.

[/edit]
 
Share this answer
 
v2
Comments
Member 15627495 20-May-23 8:30am    
for the need to cover, it seems 'StringBuilder' is more easy to use

https://learn.microsoft.com/en-us/dotnet/api/system.text.stringbuilder.append?source=recommendations&view=net-7.0
Richard MacCutchan 20-May-23 9:17am    
Yes, but that is only available in .NET projects; this one is clearly unmanaged C++.
Michael Haephrati 20-May-23 12:44pm    
I still can't find out how to solve it.
Tried:
void WriteLogFile(const std::string& filename, const char* file, int line, const std::string_view format, auto... args)
{
std::ofstream logFile(filename, std::ios_base::app);
if (!logFile)
{
std::cerr << "Failed to open log file." << std::endl;
return;
}

auto now = std::chrono::system_clock::now();
std::time_t currentTime = std::chrono::system_clock::to_time_t(now);
const std::string_view fmt_msg{ format };
std::string message = std::format("{} [{}:{}] - {}",
std::ctime(¤tTime),
file,
line,
std::format(fmt_msg, args...));

logFile << message << std::endl;
std::cout << message << std::endl;

logFile.close();
}
Richard MacCutchan 20-May-23 12:50pm    
As I mentioned above, the compiler is complaining about the inner std::format call. And having looked at some of the documentation it would seem that the format parameter in that call is not a constant. So you need to change the method to not use a std::string_view, or somehow convert that to a fixed string before you try and use it in the call. Exactly how you achieve this will depend on what this method is trying to achieve.
Member 15627495 20-May-23 12:48pm    
can you show the output expected please ?
Here is the solution I found. The only thing I don't like in it is the use of
C++
coffee_format[]
being a global variable.

#include <chrono>
#include <fstream>
#include <iostream>
#include <format>

template<const char* format, typename... Args>
void WriteLogFile(const std::string& filename, const char* file, int line, Args&&... args)
{
    std::ofstream logFile(filename, std::ios_base::app);
    if (!logFile)
    {
        std::cerr << "Failed to open log file." << std::endl;
        return;
    }

    auto now = std::chrono::system_clock::now();
    std::time_t currentTime = std::chrono::system_clock::to_time_t(now);
    std::string message = std::format(format, std::ctime(¤tTime), file, line, std::forward<Args>(args)...);

    logFile << message << std::endl;
    std::cout << message << std::endl;

    logFile.close();
}

constexpr char coffee_format[] = "{} [{}:{}] - Brewing a cup of {} with {}ml of milk at {} degrees.";

void BrewCoffee(const std::string& coffeeType, int milkAmount, int temperature)
{
    WriteLogFile<coffee_format>("coffeeMachine.log", __FILE__, __LINE__, coffeeType, milkAmount, temperature);
}

int main()
{
    BrewCoffee("Espresso", 0, 90);
    BrewCoffee("Cappuccino", 150, 85);

    return 0;
}


Update:
Actually its not a good solution because it misses the idea of a function such as WriteLogFile() which is supposed to have the flexibility to be used, for example, like this:

int main() // #O
{
    WriteLogFile("coffeeMachine.log", __FILE__, __LINE__,
        "Program started as {}.",
        std::chrono::system_clock::now()); // This line won't compile
    BrewCoffee("Espresso", 0, 90); 
    BrewCoffee("Cappuccino", 150, 85); 

    return 0; 
}


This can be solved by using a Templated Functor and a lambda

#include <chrono>
#include <fstream>
#include <iostream>
#include <format>

template<typename FormatFunctor, typename... Args>
void WriteLogFile(const std::string& filename, const char* file, int line, FormatFunctor formatFunctor, Args&&... args)
{
    std::ofstream logFile(filename, std::ios_base::app);
    if (!logFile)
    {
        std::cerr << "Failed to open log file." << std::endl;
        return;
    }

    auto now = std::chrono::system_clock::now();
    std::time_t currentTime = std::chrono::system_clock::to_time_t(now);
    std::string message = formatFunctor(std::ctime(¤tTime), file, line, std::forward<Args>(args)...);

    logFile << message << std::endl;
    std::cout << message << std::endl;

    logFile.close();
}

void BrewCoffee(const std::string& coffeeType, int milkAmount, int temperature)
{
    auto formatFunctor = [](auto... args) 
    {
        return std::format("{} [{}:{}] - Brewing a cup of {} with {}ml of milk at {} degrees.", args...);
    };
    WriteLogFile("coffeeMachine.log", __FILE__, __LINE__, formatFunctor, coffeeType, milkAmount, temperature);
}

int main()
{
    auto startFormatFunctor = [](auto... args) 
    {
        return std::format("{} [{}:{}] - Program has started at {}.", args...);
    };

    auto now = std::chrono::system_clock::now();
    std::time_t currentTime = std::chrono::system_clock::to_time_t(now);
    WriteLogFile("coffeeMachine.log", __FILE__, __LINE__, startFormatFunctor, std::ctime(¤tTime));

    BrewCoffee("Espresso", 0, 90);
    BrewCoffee("Cappuccino", 150, 85);

    return 0;
}
 
Share this answer
 
v4
Comments
Rick York 22-May-23 0:19am    
In this situation I would use a va_list and vfprintf. That would also require fopen and fclose to use a FILE object but that's OK.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900