3

I'm writing a program that reads structures from a file. For debugging purposes, it would be very convenient if I could have a compile-time toggle that prints the names and values of everything read which could be disabled for better performance/code size in the production version. In C, I could use the preprocessor like such to accomplish this:

#ifdef DEBUG
  #define READ(name, in) { name = read(in); printf("#name: %d\n", name); }
#else
  #define READ(name, in) { name = read(in); }
#endif

void myreader(mystream_t *in)
{
    int a, b, c;

    READ(a, in);
    READ(b, in);
    READ(c, in);
}

Is there any way I can reproduce this construct? I thought about this:

private static final boolean DEBUG_ENABLED = true;

private int debugRead(MyInputStream in, String name) {
    int val = in.read();

    if (DEBUG_ENABLED) {
        System.out.println(String.format("%s: %d", name, val));
    }
    return val;
}

public MyReader(MyInputStream in) {
    int a, b, c;

    a = debugRead(in, "a");
    b = debugRead(in, "b");
    c = debugRead(in, "c");
}

However, this requires me to type the name of all the variables twice as well as storing the strings corresponding to all the names even on the release version. Is there a better approach to this?

EDIT: The biggest concern I have is code verbosity. The last thing I want is to have my code cluttered with debug/print/trace statements that obscure the actual reading logic.

user2363399
  • 187
  • 1
  • 7
  • 1
    what about a logger? with level options? debug, info , fatal , error.. – nachokk Aug 23 '13 at 23:34
  • 1
    There is not much you can do. #defines create actual source code for the compiler. That concept does not exist in Java (there are preprocessors and you can generate code but not that easily) – zapl Aug 23 '13 at 23:38
  • @Kowser The solution proposed in that question is interesting, but the biggest drawback is (1) needing to store the name of the variable being read in the bytecode, and (2) needing to pass it around. – user2363399 Aug 23 '13 at 23:40
  • 1
    Well, there is no such concept of `PreProcessor` in java. There is `Annotation` unfortunately not close enough. – Kowser Aug 23 '13 at 23:43

2 Answers2

0

I'm not sure if this is an apples to apples solution, but one thing that's idiomatic in Java is to use a logging framework. With it, you can execute log.debug wherever you might need debugging. SLF4j is a common facade for logging frameworks. You could just use it with JUL logging.

Usually you leave the logging code there and you configure the logger externally to either print or not print the messages.

If you're using SLF4j, the debug message will look like this:

log.debug("Setting the creation timestamp to {}", timestamp);

Most loggers can be configured to tell you what time, class and method the logging message came from.

This has some pros and cons compared to what you're used to.

Cons

  • I have to admit, this will take a lot of effort to learn when all you really want right now is a System.out.println.
    • Most of the loggers are configured on the classpath. The classpath is a non-trivial part of java to learn but you will have to learn it eventually anyway. It's really important to understand.
  • It won't automatically print out the name of the variable passed in. AFAIK, you'll have to write that detail yourself

Pros

  • Using a logger is very robust. You can leave the code there for production and dev mode and just configure the verbosity appropriately
  • It can automatically print out the context of the class/method and date of the message and other things like that
  • You can configure the output in lots of different ways. For example, "output to the console and log.txt but when that file becomes > 100mb, rollover the old data to log2.txt and keep at most 5 log files."
Daniel Kaplan
  • 62,768
  • 50
  • 234
  • 356
  • Could you give an example of using logging that doesn't increase code verbosity? For example, if I did `a = in.read(); log.debug("read a: %d", a);` for all my reads, my code would quickly become unreadable. And if I put the logging+reading in a method, I would still need to pass the name of the variable to the method, or else the logging would be useless. – user2363399 Aug 23 '13 at 23:37
  • Nitpick: Better to use `log.debug("Setting the creation timestamp to {}", timestamp);` (avoid string concat and parameter evaluation unless debugging is actually turned on). – Thilo Aug 23 '13 at 23:42
0

To the general problem of emulating C/C++ macros in Java, there are several solutions. The particular case of logging is usually resolved in a simpler way. The simplest, conceptually closest, and puristic form is abstracting the macro in an interface and producing alternative implementations:

public class Sample {

    class mystream_t {
    }
    public int read(mystream_t is) {
        return 0 ;
    }
    static final boolean DEBUG= false ; 

    interface ReadType {
        public void apply(int[] name,mystream_t in);
    }
    ReadType READ; {
        if( DEBUG ) {
            READ= new ReadType(){
                public void apply(int[] name,mystream_t in) {
                    name[0]= read(in) ; System.out.printf("#name: %d\n",name);
                }
            };
        } else {
            READ= new ReadType(){
                public void apply(int[] name,mystream_t in) {
                    name[0]= read(in) ;
                }
            };
        }
    }
    void myreader(mystream_t in) {
        int[] a= new int[1], b= new int[1], c= new int[1];
        READ.apply(a, in);
        READ.apply(b, in);
        READ.apply(c, in);
    }
}

This makes use of a simple, static form of code injection. I tried to make the code as close as possible to the original.

The second most relevant way of emulating C/C++ macros in Java requires Annotations and Annotation Processing. It's even closer to C/C++ macros, but requires more effort and resorts to a mechanism that could not be considered pure part of the language.

And the third one is using an Aspect-Oriented Programming framework like AspectJ.

Mario Rossi
  • 7,651
  • 27
  • 37
  • 1
    Is this written in Java? That won't compile – Daniel Kaplan Aug 23 '13 at 23:50
  • @tieTYT Please do a minimum effort and focus on the concepts behind. Once you have corrected any mistake, your are welcome to edit my answer and post it there if not too long nor changing the answer essentially. – Mario Rossi Aug 23 '13 at 23:53
  • 1
    @MarioRossi: I don't think you can assign to a local variable in Java outside of its scope at all. Or get to a local variable name dynamically. Please do a minimum effort and focus on that. – Thilo Aug 23 '13 at 23:55
  • @Thilo Sorry, what local variable? READ? Of course it is in scope! What are you talking about? – Mario Rossi Aug 24 '13 at 00:01
  • `int a, b, c; READ.do(a, in);` There is no way this will assign to `a`. – Thilo Aug 24 '13 at 00:03
  • @Thilo, OMG, the bottom of the question is completely different. But I'll make the change. – Mario Rossi Aug 24 '13 at 00:08
  • compiling / running http://ideone.com/1Gtinq – zapl Aug 24 '13 at 00:14
  • @Thilo What were you saying about local variables assigned out of scope? Learned a bit Java here? – Mario Rossi Aug 24 '13 at 00:19