Void variables

J

Joe Pfeiffer

Raj Pashwar said:
OK, I will explain, but I didn't think these details are relevant.

The program has a LOG functionality. This may be enabled, then log_t will
be FILE* pointing to the LOG location. Or it may be unenabled when log_t
must be void typed.

How on earth could something like this have any hope of working? If
log_t is void, then anything you pass it to that expects a FILE* will
fail at compile time. Or were you also hoping that you could pass a
void to something expecting other types without error (in analogy to
being able to use void*'s in pointer assignments)?

Do you really want your log_t to be a void, or do you want to point it
to NULL to disable logging? I've normally enabled/disabled logging with
a boolean (or a mask of booleans to enable/disable different log
statements), or by having LOG macroes that are either #defined as doing
something or as being empty.
It appears a workaround may be needed here.

This isn't even a workaround, it's doing something sensible...
 
E

Eric Sosman

]
One thing remains UNclear: What are you trying to achieve?
The meaningless `void x;' (in whatever spelling) won't help, so simply
repeating that it doesn't gets us nowhere. Tell us what you want to
accomplish, and someone will quite likely suggest an approach that will
get you nearer your goal.

Assuming you take the trouble to read the suggestion, that is.

OK, I will explain, but I didn't think these details are relevant.

Six days from the first post, do you still trust your judgement?
The program has a LOG functionality. This may be enabled, then log_t will
be FILE* pointing to the LOG location. Or it may be unenabled when log_t
must be void typed.

Okay: That's a non-starter.
It appears a workaround may be needed here.

Several other approaches are possible. One is to keep a FILE*
variable for logging, but to set the variable NULL when you don't
want log output. Another is to use a FILE* for logging, but to
open it to a system-specific destination that just discards its
data, like "/dev/null" or "NUL:". Yet another is to control the
logging through another variable altogether, leading to the
possibility of different logging levels like "errors only,"
"errors and warnings only," "all messages of all severities."
 
J

James Kuyper

OK, I will explain, but I didn't think these details are relevant.

You're wrong; they're very relevant. You want to do something that can't
be done. Until you tell us why you want to do this, no one can tell you
anything other than "it can't be done". With those reasons, alternatives
can be suggested.
The program has a LOG functionality. This may be enabled, then log_t will
be FILE* pointing to the LOG location. Or it may be unenabled when log_t
must be void typed.

It is never the case that "log_t MUST be void typed"; that's a choice
you made, and it doesn't work. You can't define a object with void type,
but even if you could, you wouldn't be able to perform any operations on
such an object. Therefore, in order for your program to work properly,
even if you could define an object with void type, your program would
still have to be written in such a way as to avoid doing anything with
it. That's being the case, what's the point of defining such an object
in the first place?

The key thing you really need is not the ability to create an object of
type void; what you need is a way of changing which code is executed,
based upon whether or not the LOG functionality is enabled.

Prior to 2011, C didn't provide any mechanism for choosing to execute
different code depending upon what type something was. In C2011, there's
a new feature, _Generic(), that does provide such a mechanism, but using
it for this purpose would be more complicated than necessary. More
appropriate mechanisms include:

1. Using the preprocessor
// Enable logging:
#define LOG_MESSAGE(arguments) log_message(arguments)
// Disable logging:
#define LOG_MESSAGE(arguments)

Or
// Enable logging
#define LOGGING_ENABLED

#ifdef LOGGING_ENABLED
log_message(arguments)
#endif

2. Using conditional statements:

FILE *log_file;

// Enable logging:
log_file = fopen(log_file_name, "w");
// Disable logging:
log_file = NULL;

if(log_file)
log_message(log_file, arguments)

OR
#include <stdbool>
bool logging_enabled;
// Enable logging:
logging_enabled = true;
// disable logging:
logging_enabled = false;

if(logging_enabled)
log_message(arguments);
 
K

Kaz Kylheku

OK, I will explain, but I didn't think these details are relevant.

The program has a LOG functionality. This may be enabled, then log_t will
be FILE* pointing to the LOG location. Or it may be unenabled when log_t
must be void typed.

It appears a workaround may be needed here.

C does not support compile-time reasoning over types such as "if this type is
void, customize the code this way".

What you want is achieved in C using the preprocessor:

#if LOGGING_ENABLED
...
#else
...
#endif

The switch cannot be a typedef expression, but a #define directive:

#define LOGGING_ENABLED 1

You can solve this problem in C++. In the following program, you can define the
logging_enabled typedef name as either void, or FILE *. This will turn logging on
and off.

Logging is performed through the global logger object, via its logit member
function.

#include <cstdio>
#include <cstdarg>

// typedef void logging_enabled; // int means it is disabled
typedef FILE *logging_enabled; // FILE * means it is enabled

// base template for log class:
template <typename logtype> class log {
};

// log<int> specialization: constructor throws away the argument,
// and "logit" function does nothing.
template <> class log<void> {
public:
log(FILE *) { }
void logit(const char *fmt, ...) { }
};

// log<FILE *> specialization: constructor keeps FILE * object
// and "logit" function prints to that file
template <> class log<FILE *> {
private:
FILE *logfile;
public:
log(FILE *f) : logfile(f) { }
void logit(const char *fmt, ...)
{
va_list vl;
va_start (vl, fmt);
vfprintf(logfile, fmt, vl);
va_end (vl);
}
};

#include <cstdio>
log<logging_enabled> logger(stdout);

int main()
{
logger.logit("hello, world!\n");
return 0;
}

If you have questions, please ask in comp.lang.c++.
 
R

Raj Pashwar

How on earth could something like this have any hope of working? If
log_t is void, then anything you pass it to that expects a FILE* will
fail at compile time. Or were you also hoping that you could pass a
void to something expecting other types without error (in analogy to
being able to use void*'s in pointer assignments)?

Do you really want your log_t to be a void, or do you want to point it
to NULL to disable logging? I've normally enabled/disabled logging with
a boolean (or a mask of booleans to enable/disable different log
statements), or by having LOG macroes that are either #defined as doing
something or as being empty.


This isn't even a workaround, it's doing something sensible...

OK, so I didn't explain the clever bit.

In the LOG function there is an extra typecast:

....

if(severity < __log_sev_thresh) (log_t) fprintf(log,"%12s: %6s: %s
\n",timedate,__log_sev_name[severity],message);

....

Here, log has type, log_t.

The point is: if log_t is void, then the statement becomes typecast to
void, so is silently removed by the compiler. And the issue of evaluting
the void type, log, is neatly side-stepped.

Cheers Raj
 
E

Eric Sosman

[...]
OK, so I didn't explain the clever bit.

Reticence is no virtue when you're seeking help. Dribbling
out the details one by painful each may amuse you, but it just
frustrates those who try to help you.
In the LOG function there is an extra typecast:

...

if(severity< __log_sev_thresh) (log_t) fprintf(log,"%12s: %6s: %s
\n",timedate,__log_sev_name[severity],message);

...

Here, log has type, log_t.

The point is: if log_t is void, then the statement becomes typecast to
void, so is silently removed by the compiler. And the issue of evaluting
the void type, log, is neatly side-stepped.

You are wrong about the cast to void: It does not cause the
compiler to remove anything at all. The fprintf() call will still
be performed. You can test this for yourself:

#include <stdio.h>
int main(void) {
fprintf(stdout, "Hello, world!\n");
(void) fprintf(stdout, "Hello, void!\n");
return 0;
}

By the definition of the C language, this *must* produce two lines
of output, not one.

So much for "clever."

By the way, `__log_sev_thresh' and `__log_sev_name' are
identifiers that are "always reserved for any use" (7.1.3p1),
meaning that if you use them in your own code the C Standard
provides no guarantees at all about what your code may or may
not do, or even whether it will compile. Change the names.
 
J

James Kuyper

How on earth could something like this have any hope of working? If
log_t is void, then anything you pass it to that expects a FILE* will
fail at compile time. Or were you also hoping that you could pass a
void to something expecting other types without error (in analogy to
being able to use void*'s in pointer assignments)?

Do you really want your log_t to be a void, or do you want to point it
to NULL to disable logging? I've normally enabled/disabled logging with
a boolean (or a mask of booleans to enable/disable different log
statements), or by having LOG macroes that are either #defined as doing
something or as being empty.


This isn't even a workaround, it's doing something sensible...

OK, so I didn't explain the clever bit.

In the LOG function there is an extra typecast:

...

if(severity < __log_sev_thresh) (log_t) fprintf(log,"%12s: %6s: %s
\n",timedate,__log_sev_name[severity],message);

...

Here, log has type, log_t.

The point is: if log_t is void, then the statement becomes typecast to
void, so is silently removed by the compiler. ...

Typecasting is something that happens to actors. In C, there's only
casting, not type casting.

"A void expression is evaluated for its side effects." (6.3.2.2p1) "side
effects" includes causing output to occur, so the fprintf() call WILL be
evaluated before being cast to void, because it has to be in order to
produce those side effects. In particular, the compiler will notice that
your code tries to retrieve the non-existent value of 'log', and should
therefore identify this code as a constraint violation.
... And the issue of evaluting
the void type, log, is neatly side-stepped.

Not at all.
 
J

Joe Pfeiffer

Raj Pashwar said:
OK, so I didn't explain the clever bit.

Any time the best way to describe some code is "clever", You're Doing It
Wrong. In this case, you're lucky that your clever idea was based on a
bunch of misunderstandings severe enough to keep your code from
compiling; far worse is when it all works but comes back to bite the
poor guy who has to maintain the code some day....
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,079
Messages
2,570,574
Members
47,207
Latest member
HelenaCani

Latest Threads

Top