views:

286

answers:

8

I can use __LINE__ as a method parameter just fine, but I would like an easy way to use it in a function that uses strings.

For instance say I have this:

11    string myTest()
12    {
13     if(!testCondition)
14       return logError("testcondition failed");
15    }

And I want the result of the function to be:

"myTest line 14: testcondition failed"

How can I write logError? Does it have to be some monstrosity of a macro?

+3  A: 

The usual options for formatting a number into a string apply: Boost lexical_cast, ostringstream, sprintf or snprintf, etc.

Here is one of my favorite links on the topic: http://www.gotw.ca/publications/mill19.htm

Fred Larson
A: 
sprintf(newStringBuffer, "myTest line %d: testcondition failed\n", __LINE__);

should do it c style. I know that there are ways and ways of doing this with the C++ string libraries.

You could also use strcat() or strncat or any other number of C libs to do this.

cout <<"String" + __LINE__ + " another string" 

will work as well.

Michael Dorgan
The aim isn't to output it to screen but to return a string with the write data in.
John
Why not just `cout << "String" << __LINE__ << "Another string";`? No need for casting or concatenating.
Billy ONeal
@John: That's not clear from your question.
Billy ONeal
I know there are C++ string methods that overload operator+() to do string cats, I jsut wasn't sure of the syntax. I've always done these C style...
Michael Dorgan
@billy... no? I don't mention cout or console anywhere. Edit it if you want.
John
@John: You do mention "output", and it looks like you're talking about the output of the program itself.
Billy ONeal
It's still going through a function. That's the main feature. Using cout makes this a non-question :) But I clarified now anyhow.
John
Why the negative vote? Awe, someone went neg crazy, no big deal then :)
Michael Dorgan
Because that's how SO works. If I like your answer I vote it up. If it's OK but not as detailed as a great answer, no vote. If it doesn't answer the question or is a bad asnwer, I vote it down. Isn't that the point, that each person votes to try and get the answers ordered based on their preference.
John
+2  A: 

Yes, it's ugly. You need a combination of macros. Converting an integer to a string is a two-step process - here's Boost's implementation:

#define BOOST_STRINGIZE(X) BOOST_DO_STRINGIZE(X)
#define BOOST_DO_STRINGIZE(X) #X

Now you can generate a string:

logError(__FILE__ BOOST_STRINGIZE(__LINE__) "testcondition failed");   
Mark Ransom
+11  A: 

There's no reason to do any run-time work for this:

#include <iostream>

// two macros ensures any macro passed will
// be expanded before being stringified
#define STRINGIZE_DETAIL(x) #x
#define STRINGIZE(x) STRINGIZE_DETAIL(x)

// test
void print(const char* pStr)
{
    std::cout << pStr << std::endl;
}

int main(void)
{
    // adjacent strings are concatenated
    print("This is on line #" STRINGIZE(__LINE__) ".");
}

Or:

#define STOP_HAMMER_TIME(x) #x
#define STRINGIFICATE(x) STOP_HAMMER_TIME(x)

If you're a cool person like James.

GMan
Or, if you despise the word `STRINGIZE`, you can use `STRINGIFICATE` and enjoy odd stares from your coworkers.
James McNellis
@James: Duly noted.
GMan
+6  A: 

Why do you even need it as a string? What's wrong with an integer? Here are two ways you could write logError():

#define logError(str) fprintf(stderr, "%s line %d: %s\n", __FILE__, __LINE__, str)

// Or, forward to a more powerful function
#define logError(str) logError2(__FILE__, __LINE__, str)
void logError2(const char *file, int line, const char *str);

If you really need the line as a string, you can use the stringizing operator #, but because of the way macros work, you'll need to wrap it in two macros:

#define STRINGIZE(x) STRINGIZE2(x)
#define STRINGIZE2(x) #x
#define LINE_STRING STRINGIZE(__LINE__)

And now LINE_STRING is a macro that will expand to a string containing the current line number wherever it is expanded. If you only had one level of macros (i.e. if you had #define STRINGIZE(x) #x), then you would get the literal string "__LINE__" every time you expanded it, which is not what you want.

Adam Rosenfield
+1  A: 
std::string logError(const char* file, int line, const char* msg)
{
   std::ostringstream os;
   os << file << ' ' << line << ':' << msg;
   return os.str();
}

Usage:

return logError(__FILE__, __LINE__, "my error message");

You could then make a macro for this if you were so inclined:

#define LOG_ERROR(x) logError(__FILE__, __LINE__, (x))

And then the usage would be:

return LOG_ERROR("my error message");
Brian Neal
A: 

Try this?

string myTest(const int lineno)
{
  if(!testCondition)
    return logError ("testcondition failed", lineno);
}

void logError (string msg, const int lineno)
{
  clog << "line " << lineno << ": " << msg << endl;
}
Dustin
Your logError doesn't return a string.
Brian Neal
+1  A: 

His goal is to create a macro (named logError) that will automatically include the symbols necessary and do the string concatenation within the preprocessor, only using string literals.

So, combining the basically-correct answers answers thus far, let's write the macro:

#define STRINGIZE_DETAIL(x) #x
#define STRINGIZE(x) STRINGIZE_DETAIL(x)
#define logError(msg) (__FILE__ " line " STRINGIZE(__LINE__) ": " msg)

You can then use this macro anywhere to create a generic error message code in string literal format at compile time.

Note: You can also use __FUNCTION__ (or an equivalent, it varies by compiler) instead of __FILE__, if you prefer, to keep track of the function name instead of the file name.

DoctorT