I am trying to create a simple logging class with just static methods but i get troubles when i try to log some const char* variable and wonder why. It says Error: expression must have integral or unscoped enum type. I looked for it at stackoverflow and google but somehow it doesn't help me to get it right.
The logging methods are this:
void Logger::log(const char* message){
#ifdef _DEBUG
std::cout<< "[INFO]" << message << std::endl;
#endif
}
void Logger::err(const char* message){
#ifdef _DEBUG
std::cout << "[ERROR]" << message << std::endl;
#endif
}
And i am trying to call them here:
char* Config::getConfigValue(const char* name){
if (m_doc.first_node())
{
xml_node<>* node = m_doc.first_node()->first_node(name);
if (node)
{
Logger::log("Getting value: " + name + ": "+ node->value());
return node->value();
}
}
Logger::log("Getting value: " + name + " FAILED!");
return 0;
}
Am i totaly wrong with the idea of such a simple logger?
And should i do #ifdef at every point where i log or can i do it like this?
For any hints to solve this issue and to help me to get a "good" logger i would be greatful.
The problem has nothing to do with your logging. You cannot concatenate C strings using +.
"Getting value: " + name // Not going to work.
If you are in c++ anyways, I would stick with std::string when possible because that one does support + for concatenation.
char * or char const * is a pointer type, not a class or struct, thus does not support concatenation using + operator.
You're using C++, so use the C++ way of handling strings : replace const char * with const std::string &.
The expression
"Getting value: " + name + " FAILED!"
where name is const char* name is an error because there is no operator+ in global namespace that takes const char*. You have to use strcat, strcpy, etc.
In C++ you should use std::string which is very flexible ( i.e resizes dynamically):
char* Config::getConfigValue( std::string name){
//...
Logger::log("Getting value: " + name + ": "+ node->value());
}
Related
I have a method to log with the following definition:
void log(std::string s) {
std::string tag = "main";
std::cout << tag << " :" << s << std::endl;
}
I'm trying to call this method like this:
log("direction" << std::to_string(direction) << ", count: " << std::to_string(count));
direction and count are integers.
I'm getting this following error with << underlined in red:
no operator << matches these operands.
operand types are const char [10] << std::string
I have #include<string> in my header to make sure my strings are working as they should.
I tried std::string("direction") and still the issue was same.
Beginner in C++. Help would be appreciated.
operator<< isn't used for arbitrary string concatenation - it is called an "output stream operator", and it is only used in the context of std::ostream.
When you say...
std::cout << tag << " :" << s << std::endl;
...you're actually writing code roughly equivalent to:
std::cout.operator<<(tag).operator<<(" :").operator<<(s).operator<<(std::endl);
As you can see operator<< knows how to work with std::cout and std::string, but not between strings.
In order to concatenate std::string instances, you can simply use operator+:
log("direction" + std::to_string(direction) + ", count: " + std::to_string(count));
Please note that this concatenation technique is not the most efficient: you might want to look into std::stringstream or simply use std::string::reserve to avoid unnecessary memory allocations.
Substitute the << with the + operator as you are manipulating the string, not the stream:
log("direction" + std::to_string(direction) + ", count: " + std::to_string(count));
If you're determined to use the operator<< notation you need an object that understands it.
Here's such an object (I make no claims that this is a good idea):
#include <string>
#include <sstream>
#include <iostream>
void log(std::string s) {
std::string tag = "main";
std::cout << tag << " :" << s << std::endl;
}
struct string_accumulator
{
std::ostringstream ss;
template<class T>
friend string_accumulator& operator<<(string_accumulator& sa, T const& value)
{
sa.ss << value;
return sa;
}
template<class T>
friend string_accumulator& operator<<(string_accumulator&& sa, T const& value)
{
return operator<<(sa, value);
}
operator std::string () { return ss.str(); }
};
inline auto collect() -> string_accumulator
{
return string_accumulator();
}
int main()
{
int direction = 1;
int count = 1;
log(collect() << "direction" << std::to_string(direction) << ", count: " << std::to_string(count));
}
The prototype of your function is void log(std::string s);. It awaits for an std::string. So you need to pass a string to it, not a stream!
So, change this:
log("direction" << std::to_string(direction) << ", count: " << std::to_string(count));
to this:
log("direction" + std::to_string(direction) + ", count: " + std::to_string(count));
where I only changed the << operator to + operator. It will now concatenate everything inside the parentheses to a single std::string.
Your attempt implies that you wanted to pass std::ostream as the parameter. Maybe you want to read C++ Passing ostream as parameter. However, if I were you, I would just overload <<.
why don't you use:
// just include thisusing namespace std;
I need to convert a Str to char* but I sometimes lose a part of the string.
For example:
str: "/Users/seb/Pictures/Photos/20141009 - Aulani - Hawaii/20141009_083318_Richtone(HDR).jpg"
char*: /Users/seb/Pictures/Photos/20141009 - Aulani - Hawaii/20141009_083318_Ri
I'm using the code below:
void TreeWidget::CopyFilesFromLocal(QStringList pathList, QTreeWidgetItem * item) {
QString fileelt;
uint32_t Folder_id = INVALID;
MyTreeWidget* myItem = dynamic_cast<MyTreeWidget*>(item);
uint32_t destination_id = myItem->mtp_item_id;
item->setExpanded(true);
qDebug() << "**************************************";
qDebug() << "Send to PULS Start";
qDebug() << "Start of Loop to copy files";
foreach(fileelt, pathList) {
char *txt = NULL;
// char *isFileName = NULL;
qDebug() << "str: " << fileelt;
txt = strdup(m_device.convertQStr2char(fileelt));
qDebug() << "char*: " << txt;
Here is the api I use.
char *PulsDeviceMngr::convertQStr2char(QString str) {
return const_cast<char*>(std::string(str.toUtf8().constData() ).c_str());
}
Any idea ?
The pointer you return form convertQStr2char points to the internal buffer of the temporary std::string which is destroyed after the return. You thus use a dangling pointer and have undefined behavior.
Note that changing the data pointed to by std::string::c_string() through said pointer also is UB. Your const_cast is a very bad idea because it would allow exactly this even if you got the lifetime right.
You could do this instead (includes a couple of unnecessary copies, but get it working first, then worry about the rest):
char *PulsDeviceMngr::convertQStr2char(QString str) {
return strdup(std::string(str.toUtf8().constData() ).c_str());
}
Then do not forget to free the buffer. But it would probably be better to just return the std::string by value and then use .c_str() outside of your conversion function. This way, you do not need to use the non-C++ strdup and do not need to manage memory by hand, which would be error prone anyway.
You don't need an extra method
txt = strdup(fileelt.toUtf8().constData());
will work fine.
If I compile some C/C++ program using gcc with -g and/or -ggdb turned on, then if I start the program using gdb, I can print variable value inside gdb.
My question is, without gdb, can I achieve the same thing from inside the program? At runtime, given the name of the variable (represented as a runtime string), is it possible to read the debug information and then get the address of the variable as well as the type information?
Thanks.
How about about map file ? It will have info of all globals and its address. All you have to do is parse the map file and get address of the variable (python can help here).
In your program write a small routine to accept address and return value. If you are using it for logging like purpose you can do it over sockets event with a new thread so you don't interfear much with actual progarm.
I have not done this but with objdump / dltool it should be able to get the variable type information, also I am not sure how you can avoid illegal address access (ones that will cause SEG and BUS errors).
On the secod thought what is stopping you from using GDB itself ? You could use stripped version of the image to run and debug sym enabled inage to get variable type and address infomation.
Without any third-party programs or big libraries, you can add simple reflection with a few macros, depending on how ugly you'd like to have it
Here's a working sample:
#include <map>
#include <stdio.h>
#include <iostream>
#include <string>
#define DEBUG_VAR_NAMES //undef to remove the tracking
struct GlobalRecord{ //will hold info about variables
const char* name;
const char* type;
const void* addr;
};
#ifdef DEBUG_VAR_NAMES
static const GlobalRecord* global_decl( const char* name, bool set, const GlobalRecord* record ){
static std::map<const char*, const GlobalRecord*> globals; //holds pointers to all record structs
if( set )
globals[name] = record;
const auto it = globals.find( name );
if( it == globals.end() )
return nullptr; //just return nullptr if a var could not be found
else
return it->second;
}
static GlobalRecord global_init( const char* type, const char* name, void* addr, const GlobalRecord* record ) {
global_decl( name, true, record );
return GlobalRecord{ name, type, addr };
}
#define COMMA ,
#define DECLARE_GLOBAL_INIT(type, name, init) type name = init; GlobalRecord name##_rec = global_init( #type, #name, (void*)&name, & name##_rec)
#define DECLARE_GLOBAL(type, name) type name; GlobalRecord name##_rec = global_init( #type, #name, (void*)&name, & name##_rec)
#define GET_GLOBAL(name) global_decl(name, false, nullptr)
#else
#define COMMA ,
#define DECLARE_GLOBAL_INIT(type, name, init) type name = init;
#define DECLARE_GLOBAL(type, name) type name;
#define GET_GLOBAL(name) ((const GlobalRecord*) nullptr) //This is a bad idea(TM).
#endif
//SAMPLE HERE
//Declare 3 global vars for testing.
//declaring a variable is pretty simple.
//it's a macro call with <type>, <name>, <optional init value> as arguments:
DECLARE_GLOBAL_INIT( int, my_int, 5 ); //instead of: int my_int = 5;
DECLARE_GLOBAL_INIT( std::string, my_string, "hi there" ); //instead of std::string my_string = "hi there";
DECLARE_GLOBAL( std::map<int COMMA int>, my_map ); //<- commas must be replaced with a macro
void print_var( const char* name ){
std::cout << '\n';
if( GET_GLOBAL( name ) == nullptr ){
std::cout << "Var " << name << " not found.\n";
return;
}
std::cout << "Variable: " << GET_GLOBAL( name )->name << "\n";
std::cout << "The address of " << name << " is recorded as: " << GET_GLOBAL( name )->addr << "\n";
std::cout << "The type of " << name << " is recorded as : " << GET_GLOBAL( name )->type << "\n";
}
int main(int argc, char* argv[])
{
print_var( "my_int" );
print_var( "my_string" );
print_var( "my_map" );
print_var( "my_double" );
return 0;
}
Basically all global variables need to be declared as a macro such as DECLARE_GLOBAL(type, name). Some basic info about the variable will then automatically be stored inside a std::map in global_decl and can be retrieved from there.
It involves a bunch of petty hackery and should probably not be used just like that. It's more of a pointer to give you an idea of how it could be done.
The good part about that method is that the overhead is practically zero. There is no boilerplate code that would have to be called for your program to read/write variables. The bad part is macros.
If you are looking for a solution without changing your code at all, you're probably better off using gdb or a similar tool.
I have a static method that should return the next available id. Several static members in other classes call this function, so each gets a different id assigned. However, when I am printing the assigned values I dont get "id0", "id1" but just a symbol like "*". I'm using this code:
int A::i = 0; //static member
std::string A::id()
{
std::stringstream sst;
sst<< "id" << A::i;
i++;
return sst.str(); //i've tried to return a const char* too, but doesnt work either
}
//assigning id's in several classes like this:
const char* B::id = A::id().c_str();
const char* C::id = A::id().c_str();
//printing them in the main function:
std::cout << B::id << " " << C::id << "\n";
I dont understand why the code above doesnt work. When I am executing the following code the expected result "id10" is printed:
static std::string gt()
{
std::stringstream ss;
ss << "id" << 10;
return ss.str();
}
Look at
const char* C::id = A::id().c_str();
You are creating a new id string, then you take a pointer to its data (as a c-string), and inmediately after that the temporary string to which contents you are pointing gets destroyed. Simple answer: either stick to std::string or to plain int ids.
I have an application in which I need to combine strings within a variable like so:
int int_arr[4];
int_arr[1] = 123;
int_arr[2] = 456;
int_arr[3] = 789;
int_arr[4] = 10;
std::string _string = "Text " + int_arr[1] + " Text " + int_arr[2] + " Text " + int_arr[3] + " Text " + int_arr[4];
It gives me the compile error
Error C2210: '+' Operator cannot add pointers" on the second string of the expression.
As far as I can tell I am combining string literals and integers, not pointers.
Is there another concatenation operator that I should be using? Or is the expression just completely wrong and should figure out another way to implement this?
BTW I am using Visual Studio 2010
Neither C nor C++ allow concatenation of const char * and int. Even C++'s std::string, doesn't concatenate integers. Use streams instead:
std::stringstream ss;
ss << "Text " << int_arr[1] << " Text " << int_arr[2] << " Text " << int_arr[3] << " Text " << int_arr[4];
std::string _string = ss.str();
You can do this in Java since it uses the toString() method automatically on each part.
If you want to do it the same way in C++, you'll have to explicitly convert those integer to strings in order for this to work.
Something like:
#include <iostream>
#include <sstream>
std::string intToStr (int i) {
std::ostringstream s;
s << i;
return s.str();
}
int main (void) {
int var = 7;
std::string s = "Var is '" + intToStr(var) + "'";
std::cout << s << std::endl;
return 0;
}
Of course, you can just use:
std::ostringstream os;
os << "Var is '" << var << "'";
std::string s = os.str();
which is a lot easier.
A string literal becomes a pointer in this context. Not a std::string. (Well, to be pedantically correct, string literals are character arrays, but the name of an array has an implicit conversion to a pointer. One predefined form of the + operator takes a pointer left-argument and an integral right argument, which is the best match, so the implicit conversion takes place here. No user-defined conversion can ever take precedence over this built-in conversion, according to the C++ overloading rules.).
You should study a good C++ book, we have a list here on SO.
A string literal is an expression returning a pointer const char*.
std::stringstream _string_stream;
_string_stream << "Text " << int_arr[1] << " Text " << int_arr[2] << " Text " << int_arr[3] << " Text " << int_arr[4];
std::string _string = _string_stream.str();