How to add a variable to spdlog's flag - c++

I want to use spdlog for my code's logging. In my code, there is a important variable for the step in simulation, and I want it to be always displayed in my logs.
Here is the format I wants.
[log_level][the_special_variable][logger_name] messages
So how could format the logger? Or there isn't any way to do that?
Edited:
Sorry I am not good at asking a question in English.
I've read the Readme.md in spdlog's github, and i saw this
// Log patterns can contain custom flags.
// the following example will add new flag '%*' - which will be bound to a <my_formatter_flag> instance.
#include "spdlog/pattern_formatter.h"
class my_formatter_flag : public spdlog::custom_flag_formatter
{
public:
void format(const spdlog::details::log_msg &, const std::tm &, spdlog::memory_buf_t &dest) override
{
std::string some_txt = "custom-flag";
dest.append(some_txt.data(), some_txt.data() + some_txt.size());
}
std::unique_ptr<custom_flag_formatter> clone() const override
{
return spdlog::details::make_unique<my_formatter_flag>();
}
};
void custom_flags_example()
{
auto formatter = std::make_unique<spdlog::pattern_formatter>();
formatter->add_flag<my_formatter_flag>('*').set_pattern("[%n] [%*] [%^%l%$] %v");
spdlog::set_formatter(std::move(formatter));
}
but I can't understand the usage of it. It seems like it can only add a string for the custom flag. I would like to kwon if it is OK to display a int variable.

Yes, it is okay to add an int to the log message, you just have to stringify it. For example, in the format method:
auto str = std::to_string(my_special_int_variable);
dest.append(...);
The only question is how you make your int var available in the formatter. The example above assumes it's a global variable.

Related

How to replace #include <optional>

I come to you today with another question that my brain can't process by itself:
I got a cpp file that includes optional as a header file. Unfortunately, this works only on c++17 forwards, and I'm trying to compile it in c++14. This cpp file uses optional like this
std::optional<std::string> GetStringPropertyValueFromJson(const std::string& Property, const web::json::value& Json)
{
if (Json.has_field(utility::conversions::to_string_t(Property)))
{
auto& propertyValue = Json.at(utility::conversions::to_string_t(Property));
if (propertyValue.is_string())
{
return std::optional<std::string>{utility::conversions::to_utf8string(propertyValue.as_string())};
}
}
return std::nullopt;
}
and then the function is used to assign values like this:
std::string tokenType = GetStringPropertyValueFromJson("token_type", responseContent).value_or("");
std::string accessToken = GetStringPropertyValueFromJson("access_token", responseContent).value_or("");
Please help me with a proper substitution for OPTIONAL. Thanks and much love
PS: From what i've read, you can replace optional with pair somehow in order to get a similar result, but I don't really know how exactly.
PPS: I am new here so any tips on how to better write my questions or anything else are greatly appreciated :)
I guess in C++14 the optional header could be included by #include <experimental/optional>.
Change your method signature to
std::string GetStringPropertyValueFromJson(const std::string& Property, const web::json::value& Json)
and in the end just return the empty string
return "";
Then later in your code use it without std::optional::value_or:
std::string tokenType = GetStringPropertyValueFromJson("token_type", responseContent);
The logic is exactly the same and you don't use std::optional.
I see now your other question about possibility to use std::pair. Yes, you could also change your method to:
std::pair<std::string, bool> GetStringPropertyValueFromJson(const std::string& Property, const web::json::value& Json)
and return std::make_pair(valueFromJson, true) in case your json property has been found, or std::make_pair("", false) in case it was not. This also solves the problem with empty (but existing) json property.
A poor mans optional string that should be sufficient for your code is this:
struct my_nullopt {};
struct my_optional {
private:
std::string value;
bool has_value = false;
public:
my_optional(my_nullopt) {}
my_optional(const std::string& v) : value(v),has_value(true) {}
T value_or(const std::string& v) {
return has_value ? value : v;
}
};
Its a rather limited interface, for example it is not possible to set the value after construction. But it appears that you do not need that.
Alternatively you can use boost/optional.
Note that the tip you got about using a pair is just what I did above: The value and a bool. Just that std::pair is for cases where you cannot give better names than first and second (eg in generic code), but it is simple to provide a better interface than std::pair does here. With a pair the value_or would be something along the line of x.first ? x.second : "".
PS: Only in the end I realized that the code you present does not actually make use of what std::optional has to offer. As you are calling value_or(""), you cannot distinguish between a field with value "" or "" because the optional had no value. Because of that, the most simple solution is to use a plain std::string and return "" instead of std::nullopt.

Preferred way of extracting and matching protobuf message typename from an Any package

I've been using Any to package dynamic messages for protobuf.
On the receiver end, I use Any.type_url() to match the enclosed message types.
Correct me if I'm wrong, knowing that .GetDescriptor() is unavailable with Any, I still want to make the matching less of a clutter. I tried to extract the message type with brute force like this:
MyAny pouch;
// unpacking pouch
// Assume the message is "type.googleapis.com/MyTypeName"
....
const char* msgPrefix = "type.googleapis.com/";
auto lenPrefix = strlen(msgPrefix);
const std::string& msgURL = pouch.msg().type_url();
MamStr msgName = msgURL.substr(lenPrefix, msgURL.size());
if (msgName == "MyTypeName") {
// do stuff ...
}
But I'm still wondering if there are cleaner way of skipping the prefix to get the "basename" of the type URL.
Thanks!
You could try
std::string getBaseName(std::string const & url) {
return url.substr(url.find_last_of("/\\") + 1);
}
if it suits you well.
Although there are some cases, it may not explode it correctly.
Let's say you have two params as basename: http://url.com/example/2
This will get the latest, which is 2...
If you are not looking for cross-platform support, you can always go for https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/splitpath-wsplitpath?view=vs-2019

GMock: How to return mock class variable as the return value

I am trying to use GMock (google mocking framework for c++) for the first time. I have the following class:
class LocalCache
{
public:
virtual time_t GetCurrentTime() = 0;
virtual int AddEntry(const std::string key, std::string& value);
virtual int GetEntry(const std::string key, std::string& value);
};
The GetEntry method invokes GetCurrentTime call. I'd like to mock the GetCurrentTime method so that I can advance the clock in my test to test the aging out of entries which happens as part of the GetEntry call (please don't ask me why the aging is being done as part of GetEntry call... that's another discussion :( ). Here's my mock class:
class MockLocalCache : public LocalCache
{
public:
using LocalCache::GetCurrentTime;
MOCK_METHOD0(GetCurrentTime, time_t());
MockLocalCache()
: mCurrentTime(0)
{
}
void EnableFakeTime()
{
ON_CALL(*this, GetCurrentTime()).WillByDefault(Return(mCurrentTime));
}
void SetTime(time_t now) { mCurrentTime = now; }
private:
time_t mCurrentTime;
};
TEST(MockTest, TimeTest)
{
MockLocalCache mockCache;
mockCache.EnableFakeTime();
std::string key("mykey");
std::string value("My Value");
EXPECT_TRUE(mockCache.AddEntry(key, value));
mockCache.SetTime(10); // advance 10 seconds
std::string expected;
EXPECT_TRUE(mockCache.GetEntry(key, expected));
}
When I run the test, I expected the mCurrentTime value to be return by my mock GetCurrentTime function. However, I get the following error output:
GMOCK WARNING:
Uninteresting mock function call - taking default action specified at:
..../test_local_cache.cpp:62:
Function call: GetCurrentTime()
Returns: 0
Stack trace:
Would appreciate it if someone can let me know what I am doing wrong and how to fix it. Thanks in advance.
The solution to your problem is to make it in much simpler way. Just use EXPECT_CALLwhere you expect call to your mocked function:
class MockLocalCache : public LocalCache
{
public:
MOCK_METHOD0(GetCurrentTime, time_t());
};
TEST(MockTest, TimeTest)
{
MockLocalCache mockCache;
std::string key("mykey");
std::string value("My Value");
EXPECT_TRUE(mockCache.AddEntry(key, value));
EXPECT_CALL(mockCache, GetCurrentTime()).WillOnce(Return(10)); // advance 10 seconds
std::string expected;
EXPECT_TRUE(mockCache.GetEntry(key, expected));
}
Just to answer why your example did not work - with this call, the current value of your member variable is stored - later change to it has no effect:
ON_CALL(*this, GetCurrentTime()).WillByDefault(Return(mCurrentTime));
Look in google-mock-doc for difference between Return and Return(ByRef...
Probably - I did not check this, calling set member value, before calling setting this default would also work - but as I said - for your case EXPECT_CALL shall be used:
mockCache.SetTime(10); // advance 10 seconds
mockCache.EnableFakeTime();
Just for the record (and future people finding this question, like me), while PiotrNycz's answer is the best option when you can do it (keeping test values directly within tests) -- in some cases it really is necessary to return a "live" return value from a field or variable.
The appropriate documentation is here; in particular:
Return(field) doesn't work (it makes a copy of the field's current value when the action is defined)
Return(ByRef(field)) also doesn't work (it does exactly the same as above, contrary to what you might expect)
ReturnRef(field) doesn't compile (because the return type isn't a reference)
ReturnPointee(&field) does work (it returns the value as of the time the method is actually called)
Of course, you have to ensure that the pointee remains valid whenever the method is called, since it's now being used directly instead of making a copy.

QValidator prevents intermediate typing

I have a QLineEdit that accepts a string that will be evaluated at a javascript expression like "[0,3]" and connected to fire changes using editingFinished(). I added a validator so the user couldn't leave the input with a bad expression, but apparently I'm misunderstanding how the validator works, because if I return QValidator::Invalid, when the expression wouldn't be valid, user's can't type any mistakes (character won't disappear on backspace). For example, temporarily changing "[0,3]" to "[0,]" to fill in another number.
I've tried changing the validator to return to QValidator::Intermediate on bad expressions thinking that would be a happy medium letter users alter text, but setting bad text back to its previous value on unfocused or return, but that seems to let user's put in anything that want. For example, they can type in "[0," and click on something else and the input still has "[0," in it as opposed to jumping back to the way it was. Am I misunderstanding how the intermediate type works?
QValidator::Invalid 0 The string is clearly invalid.
QValidator::Intermediate 1 The string is a plausible intermediate value.
QValidator::Acceptable 2 The string is acceptable as a final result;
i.e. it is valid.
Here is my current validator, which I just put on a QLineEdit:
class PointFValidator : public QValidator
{
QValidator::State validate(QString &input, int &position) const;
void fixup(QString &input) const;
};
QValidator::State PointFValidator::validate(QString &input, int &position) const
{
try {
evalPointF(input);
} catch (std::exception &e) {
return QValidator::Invalid;
}
return QValidator::Acceptable;
}
void PointFValidator::fixup(QString &input) const
{
}
And this is what actually tests the string to see if it's formatted correctly
QPointF evalPointF(QString s)
{
//initGuile();
QScriptEngine engine;
QString program = "function frame() { return 9; }\n\n" + s;
QScriptValue value = engine.evaluate(program);
QStringList pair = value.toString().split(",");
if (pair.length() < 2)
throw std::runtime_error("invalid pointf string");
return QPointF(pair[0].toFloat(), pair[1].toFloat());
}
Is the QValidator not what I need? Is it only for typing prevention? Do I need to listen to the change event, validate it myself, and set it back if it's not valid?
So trying to compile your code didn't work for me. The constness of the virtual functions in QValidator make it pretty much impossible to get your example code to compile.
So I would (like you mentioned at the end of your question) go and set up a signal to respond to the content changes of your QLineEdit, evaluate it, and then put the output somewhere helpful.
But based on the kinds of things you put in your evalPointF function, it looks like you are just writing an IDE for javascript.
Why not use the pattern that most IDE's already have of putting issues in another window, and using font's and formatting to modify the text, instead of actually changing the text?
Hope that helps.

Ways for custom logging in C++

I am in progress of refactoring a C++ application of mine. Before I used a macro like
LOG("something interesting") // 1
LOG("something ended") // 2
LOG("foo: " << bar) // 3
My idea now was to write a Log class like so:
Log(std::string _init_message):
init_message(_init_message)
{ PrintLogLine(init_message + " ...");}
~Log()
{ PrintLogLine(init_message + " done.");}
to get "automatic" logging of specific actions (i.e. when they start, stop + additionally timings etc.) when I use it like
void ActionXYZ() {
Log log("xyz");
// do stuff
}
Where I am struggling is in defining a way to make it work for case 3). In Java I could use a method which takes one String argument since the compiler takes care of automatically string building. What possibilities do I have in C++?
Can I make it work so that I can use it like either one option?
// in "do stuff"
log("foo:" + bar); // OR
log << "foo:" << bar;
As I've mentioned in the comments, you could use Boost.Format. It also helps with the problem of int-to-string conversions, etc. It might get a bit verbose, though — to avoid calling .str() to call std::string constructor, you could make one that accepts boost::format directly.
Log log(boost::format("foo %1% bar") % 42); // with Log(boost::format)
Log log((boost::format("foo %1% bar") % 42).str()); // with only Log(std::string)
Refer to Boost.Format documentation for details.
Two immediate possibilities come to mind. The first is to take advantage of std::string appending:
Log log(std::string("foo:") + bar);
The second is to make more log constructors that take additional parameters:
Log log("foo:", bar);
You really should consider using Boost.Log for your logging. Logging can be a complex thing; it's useful to get a fully formed implementation.
You can create a logging class that inherits from std::strstream.
class Mstream : public std::strstream
{
public:
Mstream() : std::strstream(Buffer = new char[BUFLEN], BUFLEN, ios::out);
ostream& endf(ostream& s);
void Write();
private:
char* Buffer;
};
Now you can log output as,
Mstream m;
m <<"Foo"<<s<<endf;
In endf(ostream& s) you can cast ostream to Mstream and call the Write(). In Write(), you format the output and print it to console or to a file.