Click here to Skip to main content
15,898,950 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.

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.
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 ?

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