I have a program that writes occasional status updates, but I'm sure some will want it to run silently so I have provided a -s / --silent CLI option. What would be the best way to have my program abide by that parameter?
This method adds a global variable, and still executes the fprintf functions adding to the smell slightly.
FILE *outfile
if (silent) {
*outfile = NULL;}
else {
*outfile = stderr;}
fprintf (outfile, "This program can run silently");
This method also adds a global variable, and if statments littered throughout the code also adding to the smell.
if (!(silent)){
fprintf (stdout, "This program can run silently");
}
Ternary operators with a global variable might be more elegant:
(silent) ? : fprintf(stdout, "This program can run silently");
Are there better practices than any of these methods? Are there shortcomings to any one that I'm not seeing?
As Don Shankin said in his comment, the most maintainable way to do it is by wrapping fprintf() in a function that implements your filtering logic, and then have your code call the wrapper function rather than calling fprintf() directly. That way you only have to implement the filtering logic in a single location and not all over the place. Here's a program that demonstrates the technique:
#include <stdio.h>
#include <stdarg.h>
bool silent = false;
void my_fprintf(FILE * outFile, const char * fmt, ...)
{
if (silent == false)
{
va_list argsList;
va_start(argsList, fmt);
vfprintf(outFile, fmt, argsList);
va_end(argsList);
}
}
int main(int argc, char ** argv)
{
my_fprintf(stdout, "Not silent now...\n");
silent = true;
my_fprintf(stdout, "But now I'm silent, so you won't see this!\n");
silent = false;
my_fprintf(stdout, "Silent is false again!\n");
return 0;
}
... and here is the output from the program:
Not silent now...
Silent is false again!
Since it's also tagged C++, I'd like to point out that you can replace the output buffer of std::cout by calling std::cout.rdbuf(newbuf). A "devnull buff" is trivial to implement.
Related
I'd like to have access to the $HOME environment variable in a C++ program that I'm writing. If I were writing code in C, I'd just use the getenv() function, but I was wondering if there was a better way to do it. Here's the code that I have so far:
std::string get_env_var( std::string const & key ) {
char * val;
val = getenv( key.c_str() );
std::string retval = "";
if (val != NULL) {
retval = val;
}
return retval;
}
Should I use getenv() to access environment variables in C++? Are there any problems that I'm likely to run into that I can avoid with a little bit of knowledge?
There is nothing wrong with using getenv() in C++. It is defined by stdlib.h, or if you prefer the standard library implementation, you can include cstdlib and access the function via the std:: namespace (i.e., std::getenv()). Absolutely nothing wrong with this. In fact, if you are concerned about portability, either of these two versions is preferred.
If you are not concerned about portability and you are using managed C++, you can use the .NET equivalent - System::Environment::GetEnvironmentVariable(). If you want the non-.NET equivalent for Windows, you can simply use the GetEnvironmentVariable() Win32 function.
I would just refactor the code a little bit:
std::string getEnvVar( std::string const & key ) const
{
char * val = getenv( key.c_str() );
return val == NULL ? std::string("") : std::string(val);
}
If you are on Windows you can use the Win32 API GetEnvironmentVariable
On other linux/unix based systems use getenv
Why use GetEnvironmentVariable in Windows, from MSDN getenv:
getenv operates only on the data
structures accessible to the run-time
library and not on the environment
"segment" created for the process by
the operating system. Therefore,
programs that use the envp argument to
main or wmain may retrieve invalid
information.
And from MSDN GetEnvironment:
This function can retrieve either a
system environment variable or a user
environment variable.
In c++ you have to use std::getenv and #include <cstdlib>
A version of #Vlad's answer with some error checking and which distinguishes empty from missing values:
inline std::string get_env(const char* key) {
if (key == nullptr) {
throw std::invalid_argument("Null pointer passed as environment variable name");
}
if (*key == '\0') {
throw std::invalid_argument("Value requested for the empty-name environment variable");
}
const char* ev_val = getenv(key);
if (ev_val == nullptr) {
throw std::runtime_error("Environment variable not defined");
}
return std::string(ev_val);
}
Notes:
You could also replace the use of exceptions in the above with an std::optional<std::string> or, in the future, with an std::expected (if that ends up being standardized).
I've chosen safety over informativity here, by not concatenating the key into the what-string of the exception. If you make the alternative choice, try and limit copying from key to within reason (e.g. 100 characters? 200 characters?), and I'd also check these characters are printable, and sanitize those characters.
Yes, I know this is an old thread!
Still, common mistakes are, by definition, not new. :-)
The only reasons I see for not just using std::getenv(), would be to add a known default or to adopt common pattern/API in a framework. I would also avoid exceptions in this case (not generally though) simply because a non-value return is often enough a valid response for an environment variable. Adding the complexity of handling exceptions is counter-intuitive.
This is basically what I use:
const char* GetEnv( const char* tag, const char* def=nullptr ) noexcept {
const char* ret = std::getenv(tag);
return ret ? ret : def;
}
int main() {
int ret=0;
if( GetEnv("DEBUG_LOG") ) {
// Setup debug-logging
} else {
...
}
return (-1==ret?errno:0);
}
The difference between this and the other answers may seem small, but I find such small details are very rewarding when you form habits in how you code.
Just like the fact that getenv() returns a non-const pointer, which could easily lead to bad habits!
I wrote a small logging class that takes two or more arguments:
void my_log(int level, pattern [, fillins...]);
(The pattern and fill-ins are processed in a sprintf-like manner.)
The implementation of my_log begins:
if ( level < _min_level ) return;
I thought this would tidily short-circuit the call to very_elaborate_representation() ...
my_log(DEBUG, "%s", my_object.very_elaborate_representation());
when _min_level is greater than DEBUG.
But it doesn't: apparently (and not surprisingly) arguments are evaluated before the function call.
The formatting is expensive, and intended only for debugging and trouble-shooting.
Is there a clean way to solve this problem with C++11, other than wrapping the call with an if-test?
Not sure if it helps, but this is how I have previously implemented a logging module:
In the header file:
#define LOG(level,...) do {if (level >= MIN_LEVEL) log_printf(__VA_ARGS__);} while (0)
void log_printf(const char* data,...);
In the source file:
void log_printf(const char* data,...)
{
char str[64] = {0}; // You can think of other ways for allocating it
va_list args;
va_start(args,data);
vsnprintf(str,sizeof(str),data,args);
va_end(args);
printf(str);
}
In order to issue a log message, you only need to call LOG(SOME_LEVEL,some-parameters).
So we have code like:
#include "cpptk.h"
#include <stdio.h>
using namespace Tk;
void hello() {
puts("Hello C++/Tk!");
}
int main(int, char *argv[])
{
static char* str = "button .a -text "Say Hello ppure TCL"\n"
"pack .a\n";
init(argv[0]);
button(".b") -text("Say Hello") -command(hello);
pack(".b") -padx(20) -pady(6);
runEventLoop();
}
imagine str is complex tcl code. We want to feed it to C++/Tk as a string. Also we want to have it exequted in the same TCL vm our general C++/Tk programm with gui we created in C++/Tk code runs. So the result of this code would be 2 buttons inside a window.
How to do such thing?
How to do such thing?
Have you got access to the Tcl_Interp* handle used inside C++/Tk? If so (and assuming here you've got it in a variable called interp) use:
int resultCode = Tcl_Eval(interp, str);
Next, check the resultCode to see if it is TCL_OK or TCL_ERROR (other values are possible, but uncommon in normal scripts). That tells you the interpretation of the “result”, which you get like this:
const char *result = Tcl_GetString(Tcl_GetObjResult(interp));
If the result code says its an error, result is now an error message. If it was ok, the result is the output of the script (NB: not what was written to standard out though). It's up to you what to do with that.
[EDIT]: I looked this up in more detail. It's nastier than it appears, because C++/Tk hides away Tcl quite deep inside itself. In so far as I can see, you do this (untested!):
#include "cpptk.h" // might need "base/cpptkbase.h" instead
#include <string>
// This next part is in a function or method...
std::string script("the script to evaluate goes here");
std::string result = Tk::details::Expr(script,true);
I use cout statements in my program for debugging purposes. I would like to make a function that works like it, or works like printf, but is sensitive to a global variable. If this global variable is true, then it will print to screen. If it is false, then it won't print anything. Is there already a function like this? If not, then how can it be made?
Something like this:
int myPrintf(const char* format, ...)
{
if (globalCheck == 0)
return 0
va_list vl;
va_start(vl, format);
auto ret = vprintf(format, vl);
va_end(vl);
return ret;
}
va_start and va_end take the arguments in the ... and encapsulate them in a va_list. with this va_list you can then the vprintf which is a variant of printf designed exactly for this need.
Side note - usually it is bad practice to use global variables. A better thing to do is to encapsulate it in a class like this -
class ConditionalPrinter {
public:
ConditionalPrinter() : m_enable(true) {}
void setOut(bool enable) { m_enable = enable; }
int myPrintf(const char* format, ...);
private:
bool m_enable;
}
and then to check m_enable instead of the global variable.
Usage of this looks like this:
ConditionalPrinter p;
p.myPrintf("hello %d", 1); // printed
p.setOut(false);
p.myPrintf("hello2 %d", 1); // not printed
....
Don't write it yourself. Doing it right is much harder then you think. Even harder when you need threads and efficiency. Use one of existing logging libraries like:
glog: http://code.google.com/p/google-glog/ (I prefer this - it is lightweight and do what it needs to do)
Log4cpp http://log4cpp.sourceforge.net/ (powerful and configuration compatible with popular java logging)
... add your favorite to this wiki
As someone else said, there are several good logging frameworks available. However, if you want to roll your own, the first thing to note is that cout isn't a function, it's a stream. The function is operator<<. What you can do is something like the following:
/* trace.h */
extern ostream debug;
void trace_init();
void trace_done();
/* trace.cpp */
#include "trace.h"
ostream debug(cout.rdbuf());
static ofstream null;
void trace_init()
{
null.open("/dev/null");
if(output_is_disabled) { // put whatever your condition is here
debug.rdbuf(null.rdbuf());
}
}
void trace_done()
{
null.close();
}
You might have to adjust a bit if you're on a platform without /dev/null. What this does is let you write
debug << "here's some output << endl;
and if you have the output enabled, it will write to cout. If not, it will write to /dev/null where you won't see anything.
For that matter, you could just set cout's rdbuf to somewhere where you won't see that output, but I would find that to be a really bad idea. Creating new streams gives you a lot more flexibility in controlling your output.
This is with reference to the question I asked earlier -
What is the correct way to declare and use a FILE * pointer in C/C++?
MyFile.h
char sMsg[712] = "";
#define STD_MSG(string) \
fprintf (stderr, string)
#define ERR_MSG(fp, string) \
fprintf (fp, "%s\n", string);\
fflush (fp)
MyFile.C
#include "PdmTestClnt.h"
//---------------------------------------------------------------
// ** Global variables
//---------------------------------------------------------------
FILE * fpErr = NULL;
funcxyz() {
//FILE * fpErr1 = NULL;
sprintf (sMsg, "************ CHECKING FOR THE CRASH ************. \n");
ERR_MSG (fpErr, sMsg);
//ERR_MSG (fpErr1, sMsg);
}
//========================================================================
// Main
//========================================================================
integer main (integer argc, char ** argv)
{
//FILE * fpErr = NULL;
if (!(fpErr = sysFopen (sErrFileName, "a+")))
{
sprintf (sMsg,"Error in opening file %s", sErrFileName);
STD_MSG (sMsg);
}
// Log in the error file
sprintf (sMsg, "Log into the error file. \n");
ERR_MSG (fpErr, sMsg);
funcxyz();
}
If the File pointer is declared global it works. But if it is declared local it results in Memory fault(coredump).
Ran on:
HP Unix Itanium
aCC compiler (C++ Compiler)
Can somebody explain the behaviour?
Edit: Sorry for not editing the question. I now understand the problem with printf()/fprintf(). I showed the results for printf()/fprintf() with Dev C++ compiler on Windows in my answer. But when I run it on HP Unix with aCC compiler it ignores %s completely and always prints the string correctly. So how do I ask my architect to change it without showing him it memory fault on Unix?
Thanks.
Assuming by local you mean in funcxyz() uncommenting fpErr1, it segfaults because you don't open a file. You can't just use NULL there, where would you expect the data to go?
I would suggest writing funcxyz to take fpErr1 as a parameter, e.g.:
funcxyz(FILE *fpErr1) {
//FILE * fpErr1 = NULL;
sprintf (sMsg, "************ CHECKING FOR THE CRASH ************. \n");
ERR_MSG (fpErr1, sMsg);
//ERR_MSG (fpErr1, sMsg);
}
And then calling it from main like:
...
funcxyz(fpErr);
...
This information is irrelevent to the question. I should read more carefully before I answer. =X
Your problem is that you're shadowing the fpErr on the global scope with the one in the local scope. For example:
int var = 0;
void print_var() {
printf("print_var: %d\n", var);
}
int main() {
int var = 42;
printf("main: %d\n", var);
print_var();
return 0;
}
If you run the code, the output should be:
main: 42
print_var: 0
In your case, fpErr has a value of NULL (0) and thus the file I/O functions try accessing data at NULL, which causes the segmentation fault.
Don't define variables in headers - it is a nasty habit to get into.
Remove the initializer from sMsg[] in the header, and prepend 'extern' - and do define the variable, with initializer, in an appropriate source file (usually MyFile.c if the header is MyFile.h). This really matters when MyFile.h is used by several source files - and if it is used by just one source file, why were you using a header in the first place?
Your code also includes 'PdmTestClnt.h' and not MyFile.h - should we assume that MyFile.h is what you meant to include?
funcxyz() has no return type - it won't compile in C++ or under a strict C99 compiler. Why does the function format into sMsg, and then use fprintf() to copy the string? fprintf() can do the whole job (and then some).
Why you need a global definition
When you have a global variable, the code in main() initializes it by calling fopen(), and the other functions can use the initialized value. That's convenient. When you have a local variable, you have to initialize it. That's a pain because you'd end up opening the file many times which has numerous unwanted side effects - too many file handles in use, you have to close them too, and you probably keep truncating the output already in the file. To avoid that, pass the file pointer to the functions - or accept that a global is OK. Think about it - in some shape or form, the names stdin, stdout and stderr refer to global variables too.
void funcxyz(FILE *fp)
{
sprintf(sMsg, "************ CHECKING FOR THE CRASH ************. \n");
ERR_MSG (fpErr, sMsg);
}
int main(int argc, char **argv)
{
FILE *fpErr = NULL;
if ((fpErr = sysFopen(sErrFileName, "a+")) != 0)
{
sprintf(sMsg,"Error in opening file %s", sErrFileName);
STD_MSG(sMsg);
}
// Log in the error file
sprintf(sMsg, "Log into the error file. \n");
ERR_MSG(fpErr, sMsg);
funcxyz(fpErr);
return(0);
}