How can I read spdlog output in a google test - c++

I am on the latest commit of spdlog (there is an issue regarding std output, apparently resolved), and am switching my output from std::cout to spdlog.
My google tests redirect std::cout so I can check the output of stub functions:
class MyTest : public testing::Test
{
protected:
void SetUp() override
{
sbuf = std::cout.rdbuf();
std::cout.rdbuf(buffer.rdbuf());
auto console = spdlog::stdout_color_mt("console");
auto err_logger = spdlog::stderr_color_mt("stderr");
}
void TearDown() override
{
std::cout.rdbuf(sbuf);
}
std::stringstream buffer;
std::streambuf* sbuf;
}
then use as follows inside a test;
doSomethingThatWritesToStdOut();
std::string teststr = buffer.str();
EXPECT_TRUE(teststr.find("Some output string") != std::string::npos);
This doesn't work when I change the content of doSomethingThatWritesToStdOut to
spdlog::get("console")->debug("Some output string\n");
The teststr value is empty..
If I do the following
spdlog::get("console")->debug("Some output string\n");
std::cout << "Some output string\n";
Then I can see one instance of "Some output string" in teststr. How can I capture the output of this logger (or change the logger) so I can test in google tests?

As I've mentioned in the comments, I had thought spdlog output to std::cout due to an earlier issue, but in fact that was related to stdout.. (facepalm)
This is nice and easy, it turns out! By using an ostream_sink, the output can be sent to a specified ostream;
I set a logger up in my test SetUp() function as follows
auto ostream_logger = spdlog::get("gtest_logger");
if (!ostream_logger)
{
auto ostream_sink = std::make_shared<spdlog::sinks::ostream_sink_st>(_oss);
ostream_logger = std::make_shared<spdlog::logger>("gtest_logger", ostream_sink);
ostream_logger->set_pattern(">%v<");
ostream_logger->set_level(spdlog::level::debug);
}
spdlog::set_default_logger(ostream_logger);
where _oss is a std::ostringstream.
Then my tests just look at the contents of _oss, and clear it after each check:
std::string test = _oss.str();
// check the derived class is constructed
EXPECT_TRUE(test.find("Constructing test class") != std::string::npos);
_oss.str("");
The existing code using spdlog::debug, spdlog::trace etc doesn't need changing at all.

For me, the accepted answer didn't work, because some functions would get the logger by name, instead of the default logger.
If you don't need to worry about thread safety, and you're in a similar situation, then you can simply change your logger's sink, instead of creating a new logger:
struct LoggerState {
spdlog::sink_ptr oldSink {nullptr};
spdlog::level::level_enum oldLevel;
};
LoggerState redirectLogger(
std::shared_ptr<spdlog::logger>& log,
std::ostringstream& oss,
spdlog::level::level_enum newLevel
){
LoggerState ls;
ls.oldLevel = log->level();
std::vector<spdlog::sink_ptr>& sinks { log->sinks() };
assert(sinks.size() == 1);
ls.oldSink = std::move(sinks[0]);
sinks[0] = std::make_shared<spdlog::sinks::ostream_sink_st>(oss);
log->set_pattern( "[%l] %v" );
log->set_level(newLevel);
return ls;
}
void resetLogger(
std::shared_ptr<spdlog::logger>& log,
const LoggerState& ls
){
log->sinks()[0] = std::move(ls.oldSink);
log->set_level(ls.oldLevel);
}
Use it in your code as such:
std::shared_ptr<spdlog::logger> yourLogger { /* get your logger */ };
std::ostringstream oss;
LoggerState oldState = redirectLogger(yourLogger, oss, spdlog::level::warn);
/* do something that produces logger output */
resetLogger(yourLogger, oldState);
/* now oss.str() holds the captured output,
* and your old logger should be good as new */

Related

Debugger goes to allocator.h instead of to method body when stepping into

The following piece of test code is causing me trouble. It compiles ok and tests are passed, but when debugging and trying to step into function AddPizza in line (*) or (**) , it takes me to allocator.h to line allocator() throw() { } and then it continues below. So it doesn't take me inside the method to inspect if everything is all right. This doesn't happen for instance with the previous line with the method AddIngredient. What is going on, is there something wrong with my implementation of AddPizza or some other method that is causing this behavior?
By the way I am using Qtcreator on Windows 10.
TEST(TestPizzeria, TestPizza)
{
Pizzeria pizzeria;
try
{
pizzeria.AddIngredient("Tomato", "Red berry of the plant Solanum lycopersicum", 2);
pizzeria.AddIngredient("Mozzarella", "Traditionally southern Italian cheese", 3);
pizzeria.AddPizza("Margherita", vector<string> { "Tomato", "Mozzarella" });//(*)steping into it takes me to allocator.h to line `allocator() throw() { }` and then it continues below
pizzeria.AddPizza("Marinara", vector<string> { "Tomato" });
}
catch (const exception& exception)
{
FAIL();
}
try
{
pizzeria.AddPizza("Margherita", vector<string> { "Tomato", "Mozzarella" });// (**)also here
FAIL();
}
catch (const exception& exception)
{
EXPECT_THAT(std::string(exception.what()), Eq("Pizza already inserted"));
}
}
I report here both AddPizza and AddIngredient methods and all necessary methods just in case:
class Ingredient {
public:
string Name;
int Price;
string Description;};
class Pizza {
vector<Ingredient> ingredients;
public:
string Name;
void AddIngredient(const Ingredient& ingredient){ingredients.push_back(ingredient);}
};
class Pizzeria {
map<string, Ingredient> mapNameToIngredient;
map<string, Pizza> mapNameToPizza;
void AddPizza(const string &name, const vector<string> &ingredients)
{
if(mapNameToPizza.find(name) != mapNameToPizza.end())
{
throw runtime_error("Pizza already inserted");
}
else
{
Pizza pizza;
pizza.Name = name;
vector<string> ingredientss = ingredients;
for(vector<string>::iterator it = ingredientss.begin(); it != ingredientss.end(); it++)
{
Ingredient ingredient;
ingredient = FindIngredient(*it);
pizza.AddIngredient(ingredient);
}
mapNameToPizza[name] = pizza;
}
}
void AddIngredient(const string &name, const string &description, const int &price)
{
if(mapNameToIngredient.find(name) != mapNameToIngredient.end())
{
throw runtime_error("Ingredient already inserted");
}
else
{
Ingredient ingredient;
ingredient.Name = name;
ingredient.Price = price;
ingredient.Description = description;
mapNameToIngredient[name] = ingredient;
}
}
const Ingredient &FindIngredient(const string &name) const
{
auto it = mapNameToIngredient.find(name);
if(it != mapNameToIngredient.end())
{
return it->second;
}
else
{
throw runtime_error("Ingredient not found");
}
}
};
GTest use macros and global variables heavily, it's the logic of the GTest framework itself, if you try debugging with step-in, you will most likely enter the assist code other than your test code.
So it's recommended to add a breakpoint in the first line of TEST body, as #1201ProgramAlarm mentioned in the comment.
You can use g++ -E to see the code after preprocessing of the original c++ code:
#include <gtest/gtest.h>
TEST(foo, bar) { ASSERT_EQ(true, true); }
Even for this one-line test, we get one 73533 lines file after preprocessing. I have extracted the tail part and removed some file name and line number information, then we get code snippet as below(It may be different from yours since the compiler and GTest version may be different)
static_assert(sizeof("foo") > 1, "test_suite_name must not be empty");
static_assert(sizeof("bar") > 1, "test_name must not be empty");
class foo_bar_Test : public ::testing::Test {
public:
foo_bar_Test() {}
private:
virtual void TestBody();
static ::testing::TestInfo* const test_info_ __attribute__((unused));
foo_bar_Test(foo_bar_Test const&) = delete;
void operator=(foo_bar_Test const&) = delete;
};
::testing::TestInfo* const foo_bar_Test ::test_info_ =
::testing::internal::MakeAndRegisterTestInfo(
"foo", "bar", nullptr, nullptr,
::testing::internal::CodeLocation("a.cpp", 3),
(::testing::internal::GetTestTypeId()),
::testing::internal::SuiteApiResolver<
::testing::Test>::GetSetUpCaseOrSuite("a.cpp", 3),
::testing::internal::SuiteApiResolver<
::testing::Test>::GetTearDownCaseOrSuite("a.cpp", 3),
new ::testing::internal::TestFactoryImpl<foo_bar_Test>);
void foo_bar_Test ::TestBody() {
switch (0)
case 0:
default:
if (const ::testing::AssertionResult gtest_ar =
(::testing::internal::EqHelper::Compare("true", "true", true,
true)))
;
else
return ::testing::internal::AssertHelper(
::testing::TestPartResult::kFatalFailure, "a.cpp", 3,
gtest_ar.failure_message()) = ::testing::Message();
}
We can see that a class is defined by the TEST macro and a global variable is defined, so a direct step in may goes into GTest's internal code or generated code from GTest's macro.

QLibrary functions work slow on first call

I'm using QLibrary to load functions from one .dll file.
I succesfully load it, succesfully resolve functions.
But when i use some function from that .dll for the first time, this function works very slow(even if it is very simple one). Next time i use it again - and the speed is just fine (immediately, as it should be).
What is the reason for such behaviour? I suspect some caсhing somewhere.
Edit 1: Code:
typedef int(*my_type)(char *t_id);
QLibrary my_lib("Path_to_lib.dll");
my_lib.load();
if(my_lib.isLoaded){
my_type func = (my_type)my_lib.resolve("_func_from_dll");
if(func){
char buf[50] = {0};
char buf2[50] = {0};
//Next line works slow
qint32 resultSlow = func(buf);
//Next line works fast
qint32 resultFast = func(buf2);
}
}
I wouldn't blame QLibrary: func simply takes long the first time it's invoked. I bet that you'll have identical results if you resolve its address using platform-specific code, e.g. dlopen and dlsym on Linux. QLibrary doesn't really do much besides wrapping the platform API. There's nothing specific to it that would make the first call slow.
There is some code smell of doing file I/O in constructors of presumably generic classes: do the users of the class know that the constructor may block on disk I/O and thus ideally shouldn't be invoked from the GUI thread? Qt makes the doing this task asynchronously fairly easy, so I'd at least try to be nice that way:
class MyClass {
QLibrary m_lib;
enum { my_func = 0, other_func = 1 };
QFuture<QVector<FunctionPointer>> m_functions;
my_type my_func() {
static my_type value;
if (Q_UNLIKELY(!value) && m_functions.size() > my_func)
value = reinterpret_cast<my_type>(m_functions.result().at(my_func));
return value;
}
public:
MyClass() {
m_lib.setFileName("Path_to_lib.dll");
m_functions = QtConcurrent::run{
m_lib.load();
if (m_lib.isLoaded()) {
QVector<QFunctionPointer> funs;
funs.push_back(m_lib.resolve("_func_from_dll"));
funs.push_back(m_lib.resolve("_func2_from_dll"));
return funs;
}
return QVector<QFunctionPointer>();
}
}
void use() {
if (my_func()) {
char buf1[50] = {0}, buf2[50] = {0};
QElapsedTimer timer;
timer.start();
auto result1 = my_func()(buf1);
qDebug() << "first call took" << timer.restart() << "ms";
auto result2 = my_func()(buf2);
qDebug() << "second call took" << timer.elapsed() << "ms";
}
}
};

How can I write a file with containing a lua table using sol2

I've settled on using lua as my config management for my programs after seeing posts like this and loving the syntax, and sol2 recently got released so I'm using that.
So my question is, how can I grab all the variables in my lua state and spit them out in a file?
say,
sol::state lua;
lua["foo"]["bar"] = 2;
lua["foo"]["foobar"] = lua.create_table();
would, in turn, eventually spit out
foo = {
bar = 2
foobar = {}
}
Is this at all possible and if so, how?
I used this serializer to serialize my table and print it out, really quite easy!
This is what I came up with
std::string save_table(const std::string& table_name, sol::state& lua)
{
auto table = lua["serpent"];
if (!table.valid()) {
throw std::runtime_error("Serpent not loaded!");
}
if (!lua[table_name].valid()) {
throw std::runtime_error(table_name + " doesn't exist!");
}
std::stringstream out;
out << table_name << " = ";
sol::function block = table["block"];
std::string cont = block(lua[table_name]);
out << cont;
return std::move(out.str());
}

C++ How to use less conditional statements?

For my assignment, I'm storing user login infos. I'm taking in a string which is the command. The command can be create, login, remove, etc. There are 10 total options, i.e 10 different strings possible. Can anyone explain a more efficient way to write this instead of 10 if and else if statements? Basically how should I format/structure things besides using a bunch of if (string == "one"), else if (string == "two"). Thank you
I expect that your lecturer would like you to extract function to another re-usable function:
string action;
command = CreateAction(action);
command.Do(...);
Ofcourse, inside you CreateAction class you still need to have the conditionals that determine which commands need to be created.
AbstractCommand CreateAction(action)
{
if (action == "login")
return LoginCommand();
else if (action == "remove")
return RemoveCommand();
..... etc etc
}
And if you really want to get rid of all the conditionals than you can create some self-registering commands but that involves a lot more code and classes......
You should look up things like Command Pattern and Factory Pattern
You can use function pointers and a lookup table.
typedef void (*Function_Pointer)(void);
void Create(void);
void Login(void);
void Remove(void);
struct Function_Option_Entry
{
const char * option_text;
Function_Pointer p_function;
};
Function_Option_Entry option_table[] =
{
{"one", Create},
{"two", Login},
{"three", Remove},
};
const unsigned int option_table_size =
sizeof(option_table) / sizeof(option_table[0]);
//...
std::string option_text;
//...
for (i = 0; i < option_table_size; ++i)
{
if (option_text == option_table[i].option_text)
{
option_table[i].p_function();
break;
}
}
Use a switch, and a simple hash-function.
You need to use a hash-function, because C and C++ only allow switching on integral values.
template<size_t N> constexpr char myhash(const char &x[N]) { return x[0] ^ (x[1]+63); }
char myhash(const string& x) { return x.size() ? x[0] ^ (x[1]+63) : 0; }
switch(myhash(s)) {
case myhash("one"):
if(s != "one") goto nomatch;
// do things
break;
case myhash("two"):
if(s != "two") goto nomatch;
// do things
break;
default:
nomatch:
// No match
}
Slight adjustments are needed if you are not using std::string.
I would recommend you to create a function for every specific string. For example, if you receive a string "create" you will call function doCreate(), if you receive a string "login" then you call function doLogin()
The only restriction on these function is that all of them must have the same signature. In an example above it was smh like this:
typedef void (*func_t) ();
The idea is to create a std::map from strings to these functions. So you wouldn't have to write 10 if's or so because you will be able to simple choose the right function from the map by the name of a specific string name. Let me explain it by the means of a small example:
typedef void (*func_t) ();
void doCreate()
{
std::cout << "Create function called!\n";
}
void doLogin()
{
std::cout << "Login function called!\n";
}
std::map<std::string, func_t> functionMap;
void initMap()
{
functionMap["create"] = doCreate;
functionMap["login"] = doLogin;
}
int main()
{
initMap();
std::string str = "login";
functionMap[str](); // will call doLogin()
str = "create";
functionMap[str](); // will call doCreate()
std::string userStr;
// let's now assume that we also can receive a string not from our set of functions
std::cin >> userStr;
if (functionMap.count(userStr))
{
functionMap[str](); // now we call doCreate() or doLogin()
}
else
{
std::cout << "Unknown command\n";
}
return 0;
}
I hope it will help you in someway=)
You can use a map which does the comparison for you.
Something like this:
Initialise map:
std::map<std::string, std::function<void(std::string&)>> map;
map["login"] = std::bind(&Class::DoLogin, this, std::placeholders::_1);
map["create"] = std::bind(&Class::DoCreate, this, std::placeholders::_1);
Receive message:
map.at(rx.msg_type)(rx.msg_data);
Handler:
void Class::DoLogin(const std::string& data)
{
// do login
}
Maybe you can create a std::map<std::string, int> and use map lookups to get the code of the command that was passed - you can later switch on that number. Or create an enum Command and have a std::map<std::string, Command> and use the switch.
Example:
enum Command
{
CREATE,
LOGIN,
...
};
std::map<std::string, Command> commandNameToCode;
// fill the map with appropriate values
commandNameToCode["create"] = Command::CREATE;
// somehow get command name from user and store in the below variable (not shown)
std::string input;
// check if the command is in the map and if so, act accordingly
if(commandNameToCode.find(input) != commandNameToCode.end())
{
switch(commandNameToCode[input])
{
case CREATE:
// handle create
break;
...
}
}

Clang Tool: rewrite ObjCMessageExpr

I want to rewrite all messages in my code,
I need replace only selectors, but I need be able to replace nested expressions
f. e. :
[super foo:[someInstance someMessage:#""] foo2:[someInstance someMessage2]];
I tried do it with clang::Rewriter replaceText and just generate new string,
but there is a problem: It would not be work if I change selectors length, because I replace nested messages with those old positions.
So, I assumed that I need to use clang::Rewriter ReplaceStmt(originalStatement, newStatement);
I am using RecursiveASTVisitor to visit all messages, and I want to copy those messages objects, and replace selectors:
How can I do that?
I tried use ObjCMessageExpr::Create but there is so meny args, I don't know how to get ASTContext &Context and ArrayRef<SourceLocation> SeLocs and Expr *Receiver parameters from the original message.
What is the proper way to replace selectors in nested messages using clang tool (clang tooling interface)?
Update:
Should I use ReplaceStmtWithStmt callback and ASTMatchFinder ?
Update:
I am using following function to rewrite text in file:
void ReplaceText(SourceLocation start, unsigned originalLength, StringRef string) {
m_rewriter.ReplaceText(start, originalLength, string);
m_rewriter.overwriteChangedFiles();
}
And I want to replace all messageExpr in code with new selector f.e:
how it was:
[object someMessage:[object2 someMessage:obj3 calculate:obj4]];
how it should be:
[object newSelector:[object2 newSelector:obj3 newSelector:obj4]];
I am using ReqoursiveASTVisitor:
bool VisitStmt(Stmt *statement) {
if (ObjCMessageExpr *messageExpr = dyn_cast<ObjCMessageExpr>(statement)) {
ReplaceMessage(*messageExpr)
}
return true;
}
I created method for generating new message expr string:
string StringFromObjCMessageExpr(ObjCMessageExpr& messageExpression) {
std::ostringstream stringStream;
const string selectorString = messageExpression.getSelector().getAsString();
cout << selectorString << endl;
vector<string> methodParts;
split(selectorString, ParametersDelimiter, methodParts);
stringStream << "[" ;
const string receiver = GetStringFromLocations(m_compiler, messageExpression.getReceiverRange().getBegin(), messageExpression.getSelectorStartLoc());
stringStream << receiver;
clang::ObjCMessageExpr::arg_iterator argIterator = messageExpression.arg_begin();
for (vector<string>::const_iterator partsIterator = methodParts.begin();
partsIterator != methodParts.end();
++partsIterator) {
stringStream << "newSelector";
if (messageExpression.getNumArgs() != 0) {
const clang::Stmt *argument = *argIterator;
stringStream << ":" << GetStatementString(*argument) << " ";
++argIterator;
}
}
stringStream << "]";
return stringStream.str();
}
void ReplaceMessage(ObjCMessageExpr& messageExpression) {
SourceLocation locStart = messageExpression.getLocStart();
SourceLocation locEnd = messageExpression.getLocEnd();
string newExpr = StringFromObjCMessageExpr(messageExpression);
const int exprStringLegth = m_rewriter.getRangeSize(SourceRange(locStart, locEnd));
ReplaceText(locStart, exprStringLegth, newExpr);
}
The problem occurs when I try to replace nested messages, like that:
[simpleClass doSomeActionWithString:string3 andAnotherString:string4];
[simpleClass doSomeActionWithString:str andAnotherString:str2];
[simpleClass doSomeActionWithString:#"" andAnotherString:#"asdasdsad"];
[simpleClass setSimpleClassZAZAZAZAZAZAZAZA:[simpleClass getSimpleClassZAZAZAZAZAZAZAZA]];
the result is:
[simpleClass newSelector:string3 newSelector:string4 ];
[simpleClass newSelector:str newSelector:str2 ];
[simpleClass newSelector:#"" newSelector:#"asdasdsad" ];
[simpleClass newSelector:[simpleClass getSimp[simpleClass newSelector]];
because messageExpression has "old" value of getLocStart(); and getLocEnd(); How can I fix it?
You can rewrite selector name by replacing only continuous parts of selector name. For example, replace only underlined parts
[object someMessage:[object2 someMessage:obj3 calculate:obj4]];
^~~~~~~~~~~ ^~~~~~~~~~~ ^~~~~~~~~
To achieve this you require only
number of selector parts - ObjCMessageExpr::getNumSelectorLocs()
their locations - ObjCMessageExpr::getSelectorLoc(index)
their lengths - ObjCMessageExpr::getSelector().getNameForSlot(index).size().
Overall, you can rewrite ObjCMessageExpr with the following RecursiveASTVisitor:
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Rewrite/Core/Rewriter.h"
namespace clang_tooling
{
using clang::SourceLocation;
class RewritingVisitor : public clang::ASTConsumer,
public clang::RecursiveASTVisitor<RewritingVisitor>
{
public:
// You can obtain SourceManager and LangOptions from CompilerInstance when
// you are creating visitor (which is also ASTConsumer) in
// clang::ASTFrontendAction::CreateASTConsumer.
RewritingVisitor(clang::SourceManager &sourceManager,
const clang::LangOptions &langOptions)
: _sourceManager(sourceManager), _rewriter(sourceManager, langOptions)
{}
virtual void HandleTranslationUnit(clang::ASTContext &context)
{
TraverseDecl(context.getTranslationUnitDecl());
_rewriter.overwriteChangedFiles();
}
bool VisitObjCMessageExpr(clang::ObjCMessageExpr *messageExpr)
{
if (_sourceManager.isInMainFile(messageExpr->getLocStart()))
{
clang::Selector selector = messageExpr->getSelector();
for (unsigned i = 0, end = messageExpr->getNumSelectorLocs();
i < end; ++i)
{
SourceLocation selectorLoc = messageExpr->getSelectorLoc(i);
_rewriter.ReplaceText(selectorLoc,
selector.getNameForSlot(i).size(),
"newSelector");
}
}
return Base::VisitObjCMessageExpr(messageExpr);
}
private:
typedef clang::RecursiveASTVisitor<RewritingVisitor> Base;
clang::SourceManager &_sourceManager;
clang::Rewriter _rewriter;
};
} // end namespace clang_tooling