6

I am currently trying to understand how the following code (http://pastebin.com/zTHUrmyx) works, my approach is currently compiling the software in debug and using gdb to step through the code.

However, I'm running into the problem that 'step' does not always tell me what is going on. Particularly unclear to me is the EXECUTE {...} which I cannot step into.

How do I go about learning what the code is doing?

   1     /*
   2     Copyright 2008 Brain Research Institute, Melbourne, Australia
   3
   4     Written by J-Donald Tournier, 27/06/08.
   5
   6     This file is part of MRtrix.
   7
   8     MRtrix is free software: you can redistribute it and/or modify
   9     it under the terms of the GNU General Public License as published by
  10     the Free Software Foundation, either version 3 of the License, or
  11     (at your option) any later version.
  12
  13     MRtrix is distributed in the hope that it will be useful,
  14     but WITHOUT ANY WARRANTY; without even the implied warranty of
  15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16     GNU General Public License for more details.
  17
  18     You should have received a copy of the GNU General Public License
  19     along with MRtrix.  If not, see <http://www.gnu.org/licenses/>.
  20
  21
  22     15-10-2008 J-Donald Tournier <d.tournier@brain.org.au>
  23     * fix -prs option handling
  24     * remove MR::DICOM_DW_gradients_PRS flag
  25
  26     15-10-2008 J-Donald Tournier <d.tournier@brain.org.au>
  27     * add -layout option to manipulate data ordering within the image file
  28
  29     14-02-2010 J-Donald Tournier <d.tournier@brain.org.au>
  30     * fix -coord option so that the "end" keyword can be used
  31
  32
  33 */
  34
  35 #include "app.h"
  36 #include "image/position.h"
  37 #include "image/axis.h"
  38 #include "math/linalg.h"
  39
  40 using namespace std;
  41 using namespace MR;
  42
  43 SET_VERSION_DEFAULT;
  44
  45 DESCRIPTION = {
  46   "perform conversion between different file types and optionally extract a subset of the input image.",
  47   "If used correctly, this program can be a very useful workhorse. In addition to converting images between different formats, it can be used to extract specific studies from a data set, extract a specific region of interest, flip the images, or to scale the intensity of the images.",
  48   NULL
  49 };
  50
  51 ARGUMENTS = {
  52   Argument ("input", "input image", "the input image.").type_image_in (),
  53   Argument ("ouput", "output image", "the output image.").type_image_out (),
  54   Argument::End
  55 };
  56
  57
  58 const gchar* type_choices[] = { "REAL", "IMAG", "MAG", "PHASE", "COMPLEX", NULL };
  59 const gchar* data_type_choices[] = { "FLOAT32", "FLOAT32LE", "FLOAT32BE", "FLOAT64", "FLOAT64LE", "FLOAT64BE",
  60     "INT32", "UINT32", "INT32LE", "UINT32LE", "INT32BE", "UINT32BE",
  61     "INT16", "UINT16", "INT16LE", "UINT16LE", "INT16BE", "UINT16BE",
  62     "CFLOAT32", "CFLOAT32LE", "CFLOAT32BE", "CFLOAT64", "CFLOAT64LE", "CFLOAT64BE",
  63     "INT8", "UINT8", "BIT", NULL };
  64
  65 OPTIONS = {
  66   Option ("coord", "select coordinates", "extract data only at the coordinates specified.", false, true)
  67     .append (Argument ("axis", "axis", "the axis of interest").type_integer (0, INT_MAX, 0))
  68     .append (Argument ("coord", "coordinates", "the coordinates of interest").type_sequence_int()),
  69
  70   Option ("vox", "voxel size", "change the voxel dimensions.")
  71     .append (Argument ("sizes", "new dimensions", "A comma-separated list of values. Only those values specified will be changed. For example: 1,,3.5 will change the voxel size along the x & z axes, and leave the y-axis voxel size unchanged.")
  72         .type_sequence_float ()),
  73
  74   Option ("datatype", "data type", "specify output image data type.")
  75     .append (Argument ("spec", "specifier", "the data type specifier.").type_choice (data_type_choices)),
  76
  77   Option ("scale", "scaling factor", "apply scaling to the intensity values.")
  78     .append (Argument ("factor", "factor", "the factor by which to multiply the intensities.").type_float (NAN, NAN, 1.0)),
  79
  80   Option ("offset", "offset", "apply offset to the intensity values.")
  81     .append (Argument ("bias", "bias", "the value of the offset.").type_float (NAN, NAN, 0.0)),
  82
  83   Option ("zero", "replace NaN by zero", "replace all NaN values with zero."),
  84
  85   Option ("output", "output type", "specify type of output")
  86     .append (Argument ("type", "type", "type of output.")
  87         .type_choice (type_choices)),
  88
  89   Option ("layout", "data layout", "specify the layout of the data in memory. The actual layout produced will depend on whether the output image format can support it.")
  90     .append (Argument ("spec", "specifier", "the data layout specifier.").type_string ()),
  91
  92   Option ("prs", "DW gradient specified as PRS", "assume that the DW gradients are specified in the PRS frame (Siemens DICOM only)."),
  93
  94   Option::End
  95 };
  96
  97
  98
  99 inline bool next (Image::Position& ref, Image::Position& other, const std::vector<int>* pos)
 100 {
 101   int axis = 0;
 102   do {
 103     ref.inc (axis);
 104     if (ref[axis] < ref.dim(axis)) {
 105       other.set (axis, pos[axis][ref[axis]]);
 106       return (true);
 107     }
 108     ref.set (axis, 0);
 109     other.set (axis, pos[axis][0]);
 110     axis++;
 111   } while (axis < ref.ndim());
 112   return (false);
 113 }
 114
 115
 116
 117
 118
 119 EXECUTE {
 120   std::vector<OptBase> opt = get_options (1); // vox
 121   std::vector<float> vox;
 122   if (opt.size())
 123     vox = parse_floats (opt[0][0].get_string());
 124
 125
 126   opt = get_options (3); // scale
 127   float scale = 1.0;
 128   if (opt.size()) scale = opt[0][0].get_float();
 129
 130   opt = get_options (4); // offset
 131   float offset = 0.0;
 132   if (opt.size()) offset = opt[0][0].get_float();
 133
 134   opt = get_options (5); // zero
 135   bool replace_NaN = opt.size();
 136
 137   opt = get_options (6); // output
 138   Image::OutputType output_type = Image::Default;
 139   if (opt.size()) {
 140     switch (opt[0][0].get_int()) {
 141       case 0: output_type = Image::Real; break;
 142       case 1: output_type = Image::Imaginary; break;
 143       case 2: output_type = Image::Magnitude; break;
 144       case 3: output_type = Image::Phase; break;
 145       case 4: output_type = Image::RealImag; break;
 146     }
 147   }
 148
 149
 150
 151
 152   Image::Object &in_obj (*argument[0].get_image());
 153
 154   Image::Header header (in_obj);
 155
 156   if (output_type == 0) {
 157     if (in_obj.is_complex()) output_type = Image::RealImag;
 158     else output_type = Image::Default;
 159   }
 160
 161   if (output_type == Image::RealImag) header.data_type = DataType::CFloat32;
 162   else if (output_type == Image::Phase) header.data_type = DataType::Float32;
 163   else header.data_type.unset_flag (DataType::ComplexNumber);
 164
 165  
 166   opt = get_options (2); // datatype
 167   if (opt.size()) header.data_type.parse (data_type_choices[opt[0][0].get_int()]);
 168
 169   for (guint n = 0; n < vox.size(); n++)
 170     if (isfinite (vox[n])) header.axes.vox[n] = vox[n];
 171
 172   opt = get_options (7); // layout
 173   if (opt.size()) {
 174     std::vector<Image::Axis> ax = parse_axes_specifier (header.axes, opt[0][0].get_string());
 175     if (ax.size() != (guint) header.axes.ndim())
 176       throw Exception (String("specified layout \"") + opt[0][0].get_string() + "\" does not match image dimensions");
 177
 178     for (guint i = 0; i < ax.size(); i++) {
 179       header.axes.axis[i] = ax[i].axis;
 180       header.axes.forward[i] = ax[i].forward;
 181     }
 182   }
 183
 184
 185   opt = get_options (8); // prs
 186   if (opt.size() && header.DW_scheme.rows() && header.DW_scheme.columns()) {
 187     for (guint row = 0; row < header.DW_scheme.rows(); row++) {
 188       double tmp = header.DW_scheme(row, 0);
 189       header.DW_scheme(row, 0) = header.DW_scheme(row, 1);
 190       header.DW_scheme(row, 1) = tmp;
 191       header.DW_scheme(row, 2) = -header.DW_scheme(row, 2);
 192     }
 193   }
 194
 195   std::vector<int> pos[in_obj.ndim()];
 196
 197   opt = get_options (0); // coord
 198   for (guint n = 0; n < opt.size(); n++) {
 199     int axis = opt[n][0].get_int();
 200     if (pos[axis].size()) throw Exception ("\"coord\" option specified twice for axis " + str (axis));
 201     pos[axis] = parse_ints (opt[n][1].get_string(), header.dim(axis)-1);
 202     header.axes.dim[axis] = pos[axis].size();
 203   }
 204
 205   for (int n = 0; n < in_obj.ndim(); n++) {
 206     if (pos[n].empty()) {
 207       pos[n].resize (in_obj.dim(n));
 208       for (guint i = 0; i < pos[n].size(); i++) pos[n][i] = i;
 209     }
 210   }
 211
 212
 213   in_obj.apply_scaling (scale, offset);
 214
 215
 216
 217
 218
 219
 220   Image::Position in (in_obj);
 221   Image::Position out (*argument[1].get_image (header));
 222
 223   for (int n = 0; n < in.ndim(); n++) in.set (n, pos[n][0]);
 224
 225   ProgressBar::init (out.voxel_count(), "copying data...");
 226
 227   do {
 228
 229     float re, im = 0.0;
 230     in.get (output_type, re, im);
 231     if (replace_NaN) if (gsl_isnan (re)) re = 0.0;
 232     out.re (re);
 233
 234     if (output_type == Image::RealImag) {
 235       if (replace_NaN) if (gsl_isnan (im)) im = 0.0;
 236       out.im (im);
 237     }
 238
 239     ProgressBar::inc();
 240   } while (next (out, in, pos));
 241
 242   ProgressBar::done();
 243 }
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Jesse RJ
  • 217
  • 4
  • 17
  • 4
    This code apparently uses some weird-*donkey* macros. To be able to understand it, we'd need to see all the headers as well. Still, this question is probably a better fit for [Code review](http://codereview.stackexchange.com/). – Angew is no longer proud of SO Apr 18 '13 at 06:35
  • You may get some insight by running just the preprocessing / macro-expanding compiler stage. For example, with GNU GCC it's `g++ -E`: then you can see what EXECUTE actually does to the code. – Tony Delroy Apr 18 '13 at 06:44
  • 5
    I'm tempted to toss you an upvote just for taking the initiative of stepping through this with a debugger, which puts you ahead of approximately 99% of the people that ask questions here lately. – WhozCraig Apr 18 '13 at 06:46
  • 2
    'Using gdb to step through the code'. This is very good. I suggest you set a *breakpoint* in line 120. Execution should then stop on line 120, and you can step from there. – john Apr 18 '13 at 07:12
  • thank you very much for the reply, in app.h is the definition of Execute, I have uploaded it here http://pastebin.com/6ZnAKxDC. but I am still unclear what it is doing. – Jesse RJ Apr 18 '13 at 07:27
  • @JesseRJ It defines a class MyApp with a run method and the main function. In the main function an instance of MyApp is created, and the run method on the MyApp instance is called. Plus a few other bits and pieces. All a bit silly I think. – john Apr 18 '13 at 07:33
  • @john, yes, I'll have to track down the run method to see what is going on... havent found it yet – Jesse RJ Apr 18 '13 at 11:37
  • 1
    Please do not suggest [codereview.se] without having read the [faq](http://codereview.stackexchange.com/faq), this is off-topic. I've voted to re-open it here as I believe this question may prove helpful to a whole range of people, not just the OP. – Adam Apr 18 '13 at 12:46
  • @Angew: Please don't recommend code to code review to try and understand it. That is what stack-overflow is for. Code review is for working code that you want to improve with best practice and a few other narrowly defined criteria. – Martin York Apr 18 '13 at 13:18
  • @LokiAstari Sorry, noted. Nevertheless, it's obviously "too localized" for stackoverflow. – Angew is no longer proud of SO Apr 18 '13 at 13:24
  • @Angew: Seriously. The question is asking how to see what is under the EXECUTE macro while debugging. How on earth do you come up with too localized. That is a common technique required for debugging and useful for everybody. You give people 10,000 points and suddenly they go wild playing with the buttons now they have power. Stop it and think. – Martin York Apr 18 '13 at 16:01
  • @LokiAstari If you bothered to read the list of people who cast the 5 votes to close, you'd find I **was not** one of them. I did not vote to close this question. My "too localized" comment was based on the fact that *other people* closed the question. I said I was sorry for suggesting Codereview inappropriately and I meant it. I am definitely not "going wild playing with the buttons." – Angew is no longer proud of SO Apr 18 '13 at 17:30
  • Thanks all for your replies. Any suggestion where else I might post this question and receive help to understand this code? – Jesse RJ Apr 18 '13 at 19:55

1 Answers1

3

As was noted in the comments, EXECUTE seems to be a macro, apparent from the context a function header (and maybe a bit more, e.g. some global variables and functions), so the part in curly braces is the function body.

To get to the definition of EXECUTE, you will have to examine the headers.

However, if you can reach some part of the code during debugging, you could insert a string or char[] at that point, giving it the stringified version of EXECUTE, so you get whatever the preprocessor will emit for EXECUTE at that position in the code.

#define STR(x) #x
#define STRINGIFY(x) STR(x)
char c[] = STRINGIFY(EXECUTE);

the two macros are a known little macro trick to get the content of any macro as a string literal. Try it out and inspect the char array in your debugger to get the content of execute.

My wild guess here: EXECUTE is the main function or a replacement for it, the OPTIONS and ARGUMENTS describe what arguments the program expects and what command line options you can pass to it. Those macros and some of the used functions and variables (get_options, argument) are part of a little framework that should facilitate the usage, evaluation and user information about command line options.

Arne Mertz
  • 24,171
  • 3
  • 51
  • 90
  • Hi Arne, thanks for the reply. I've gotten app.h which has the definition of EXECUTE, http://pastebin.com/6ZnAKxDC. This definition would be the same one produced by STRINGIFY? – Jesse RJ Apr 18 '13 at 07:29
  • @JesseRJ yes, as you see, it's only a bit more than a main function: it defines a class with an execute method, and a main function that instantiates an object of that class, calls the execute function and handles any exceptions. The function body I talked about is the implementation of the execute method. – Arne Mertz Apr 18 '13 at 07:35
  • now I see what you mean 'int main (int argc, gchar* argv[]) { \ try { \ MyApp app (...)' I see the definition of 'MyApp' and 'App', but not for 'app'. I am pretty sure 'App' and 'app' are completely separate independent classes or functions right? I will have to track down app? – Jesse RJ Apr 18 '13 at 07:46
  • no, app is a variable of type `MyApp`, initialized with `argc`, `argc`, `__command_description and so on`. You can do the following: Copy the macro definition of `EXECUTE` (lines 45 through 60 in app.h), replace the `EXECUTE` in line 119 of your code with that macro definition and get rid of the `\` - it will be the same code for the compiler (except for line numbers). Then debug and see what's really going on ;) – Arne Mertz Apr 18 '13 at 07:52
  • not sure if I followed you right, I replaced the `EXECUTE` call in the main code with the definition in the header, however I am unable to compile the code when I do so. Error and new cpp code here: http://pastebin.com/sZ9mLrpJ. – Jesse RJ Apr 18 '13 at 11:36
  • you did not only replace the `EXECUTE` but deleted the opening `{` as well. To have an easier to understand code, remove the trailing backslashes in the lines of the former macro definition. With a bit of manual indentation and some blank linkes for better readability you will get this: http://pastebin.com/Qkqcd4rA – Arne Mertz Apr 18 '13 at 11:49
  • that is a lot cleaner and easier to read, thanks. you also moved `void execute ();` inside the brackets, could you clarify that? – Jesse RJ Apr 18 '13 at 12:22
  • I did not move anything anywhere - I left everything at it's place. the `void execute();` is a usual function declaration inside the class definition, i.e. a method declaration, it's definition comes later. You do know classes, methods and the difference between declaration and definition, do you? – Arne Mertz Apr 18 '13 at 12:47
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/28442/discussion-between-jesse-rj-and-arne-mertz) – Jesse RJ Apr 18 '13 at 13:07