Default parameters for Root macro

I have written a root macro that first asks the user for several parameters. As I update this macro, running it often to see the effect of my changes, it is a nuisance to have to re-enter a bunch of parameters each time I run it. I would like to modify the macro so that some default value is assigned to each parameter should the user hit “enter” instead of typing a number. Then I can run the macro and hit enter a bunch of times to get it to start quickly. I have tried many different approaches to this, all of which work using a regular c++ compiler, but none work within root. An example of my code is:

#include "Riostream.h"

int rootws()
{
  Int_t* ptr = new Int_t;

  cout << "Enter int: ";

  cin.unsetf(ios::skipws);
  cin >> *ptr;
  if(!cin.good())
    {
      cout << "Not an integer " << endl;
    }
  else cout << "the number is " << *ptr << endl;

  delete ptr;
  return 0;
}

This is what happens in root:

[0] .L rootws.C
root [1] rootws()
Enter int: <- just hit enter here
Not an integer
(int)0
root [2] rootws()
Enter int: Not an integer <- didn’t type anything here
(int)0
root [3]

So it works the first time, but, unless I quit and re-open Root, every succesive time it appears to assume something has already been typed (or at least that an error flag is already set- I have tried resetting the error bits to no avail). It does not matter what I type the first time around, successive runs always give the result seen above.

Does anyone know how to make this work?

Thanks,

David Ticehurst

Hi,

You should call:cin.clear();to properly restore the state of std::cin.

Cheers,
Philippe.

Thanks for the reply Philippe, though I have tried using cin.clear() and it gives me the same problem. Here is the code with that included:

#include "Riostream.h"

rootws()
{
  Int_t* ptr = new Int_t;

  cout << "Enter int: ";
  
  cin.clear();

  cin.unsetf(ios::skipws);
  cin >> *ptr;
  if(!cin.good())
    {
      cout << "Not an integer " << endl;
    }
  else cout << "the number is " << *ptr << endl;

  delete ptr;
  
  cin.clear();

}

Best,

David

Hi,

You also need to ‘consume’ the characters that provokes the errors. For example:[code]#include “Riostream.h”

void rootws()
{
Int_t* ptr = new Int_t;

std::string buf;

cout << "Enter int: ";

//cout << “–” << cin.peek() << “–”;
*ptr = 99;
// cin.unsetf(ios::skipws);
cin >> *ptr;
if(!cin.good())
{
cout << "Not an integer " << *ptr << endl;
cin.clear();
cin >> buf;
}
else cout << "the number is " << *ptr << endl;

delete ptr;
} [/code]should have the behavior you want. (Note that I am not sure why you unset the skipping of white space, this seems to make thing much more complex).

Cheers,
Philippe.

Philippe:

I am unsetting the skip whitespace flag because I do not want to ignore the instance when only return is pressed (doing this should provide my macro with a default value). Your code hangs until a character or number is typed.

It seems that the problem originates with unsetting this flag, as removing that line removes the original problem.(although of course it adds the new problem of doing nothing when only return is pressed, and that’s the whole point of what I’m trying to do)

I tried resetting the flag after the if statement, but this did not work either.

Best,

David

Hi,

Since cin does not consume the white space (nor the newline) by default you need to consume them explicitly, for example:[code]#include “Riostream.h”

void rootws()
{
Int_t* ptr = new Int_t;
*ptr = 99;

cout << "Enter int: ";

cin.clear();
cin.unsetf(ios::skipws);
cout << “–” << (char)cin.peek() << “–”;
cin >> *ptr;
if(!cin.good())
{
cout << "Not an integer " << *ptr << endl;
cin.clear();
}
else cout << "the number is " << *ptr << endl;

// Consume the newline
char c;
cin >> c;
while (cin.good() && isspace© && c != ‘\n’) {
cin >> c;
}
cin.clear();
delete ptr;
}
[/code]

Cheers,
Philippe.

Philippe,

This code will do what I need. Thank you very much for your help.

Read on only FYI if you have time to spare…


This code does still have a problem if the user enters a value that is not a number and not whitespace. (This is of course irrelevant for my macro since the user should never enter anything other than those two.) When N of these characters are typed, the macro must be re-run N times before allowing new input:

root [37] rootws()
Enter int: …hey
h…Not an integer 99
root [38] rootws()
Enter int: …e…Not an integer 99
root [39] rootws()
Enter int: …y…Not an integer 99
root [40] rootws()
Enter int: …
…Not an integer 99

I do not fully understand the concept of consuming characters, but if I had to guess I’d say cin causes parts of the stream to be consumed or removed, but our three cin statements only consume integers, whitespaces, and the single next non-number, non-whitespace character. I believe using cin.getline solves this problem (provided the user doesn’t type more than 15 characters):

#include "Riostream.h"

void rootws()
{
  Int_t* ptr = new Int_t;
  *ptr = 99;
  Char_t str[15];
   
  cout << "Enter int: ";
  
  cin.unsetf(ios::skipws);
  cin >> *ptr;
   
  if(!cin.good())
    {
      cout << "Not an integer " << *ptr << endl;
      cin.clear();
    }
  else cout << "the number is " << *ptr << endl;
  
  //get next character
  Char_t c;
  cin >> c;

  //if next character is not ws, consume up
  //to the next 15 characters and newline
  if(!isspace(c))
    {
      cin.getline(str, 15, '\n');
    }

  cin.clear();    
  delete ptr;  
}

This just seems like a lot of code to do something simple. Is there no way to flush out the stream succinctly or delete then recreate the stream object?

Thanks again,

David

Hi David,

The following might help:cin.ignore( numeric_limits<int>::max() , '\n' );which should ignore the rest of the line.

Cheers,
Philippe.

I have tried this, which does not work:

#include "Riostream.h"

void rootws()
{
  Int_t* ptr = new Int_t;
  *ptr = -1;
   
  cout << "Enter int: ";
  
  cin.unsetf(ios::skipws);
  cin >> *ptr;
  
  cin.ignore(100, '\n');

  if(*ptr > 0) cout << "you typed " << *ptr << endl;
  else cout << "not int" << endl;

  cin.clear();  
  delete ptr;
}

Ticesty,

I believe that this will successfully solve your problem.

#include <iostream>
#include <string>
using namespace std;

int
main()
{
        string buf;
        cout << "Enter a number" << endl;
        getline(cin,buf);

        if (buf.compare("") == 0)
                cout << "No entry found" << endl;
        else
                cout << "You entered :" << buf.c_str() << endl;

        return 0;

}

Jeromy

If you define your macro like this:

void rootMacro (int alpha=2, int beta=3) { cout << alpha << ", " << beta << endl; }

you could then call your code

root -q 'rootMacro.C()'
root -q 'rootMacro.C(1)'
root -q 'rootMacro.C(1,2)'

Where alpha, beta would be 2,3; 1,3; and 1,2 respectively. Doing it this way means that you can call your macro via a script much more easily as well.

Cheers,
Charles

p.s. Is there a reason why you are using a pointer to an integer instead of just an integer?

No reason for the pointer to int- I was just trying anything I could think of in earlier editions of the code.

Thanks for the help,

David