Taking input from file on startup in GUI program (Chapter 16 Exercise 8)

48 views
Skip to first unread message

Osman Zakir

unread,
Aug 27, 2017, 6:34:42 PM8/27/17
to PPP-public
I'll define the callback action functions the rest of the way through after I see how to get the file into the program.  So yeah, could someone please help me see how to get the file into the program at startup?  Thanks in advance.

Would it be good to read the file in the action functions themselves?  Or should I read it in main() (but then, how would I get the data into the callback action functions?)?
chapter16ex8.cpp

Osman Zakir

unread,
Aug 28, 2017, 6:50:16 AM8/28/17
to PPP-public
I also need some help on the callback action methods themselves.  Any help on the things mentioned in the OP and this post will be much appreciated.  Thanks in advance. 

Osman Zakir

unread,
Aug 28, 2017, 2:23:59 PM8/28/17
to PPP-public
I've made some changes to it the fix stuff.  Still need to figure out how to actually write those methods.  And also how to get the input file loaded at startup.  Thanks in advance. 
chapter16ex8.cpp

Osman Zakir

unread,
Aug 31, 2017, 5:17:29 PM8/31/17
to PPP-public
I wonder if it'd be fine to have one menu for both the "from" and "to" conversions.  I'm having a hard time figuring how to do it the way it is now, but maybe if I had one button for each possible conversion, I'd be able to do it more easily.  Or if someone can help me make it work the way it is now, that'd be appreciated too.  

Osman Zakir

unread,
Sep 1, 2017, 11:09:24 AM9/1/17
to PPP-public
I'm sorry for the continuous chain of posts by me, but I need to do this.

Anyway, I have updated code attached to this message, as well as an incomplete text file.  I'm not sure if I'm writing the file correctly.  Then I'll also need to see how to read the file correctly so the program will know what conversion rate is for.

If there are any problems in what I've done so far, please tell me how to fix it.  Thanks in advance.
chapter16ex8.cpp

Osman Zakir

unread,
Sep 1, 2017, 11:14:24 AM9/1/17
to PPP-public
I was attaching the aforementioned input file to the previous message, but something happened (I guess due to lag and connection problems) and I wasn't able to post the version of the message with the text file attached.  So I'm making this post for that. 

Art Werschulz

unread,
Sep 1, 2017, 11:35:29 AM9/1/17
to PPP-public
Hi.
The text file appears to be empty.  (Both times.)

There's a lot of nearly-replicated code.  I'm wonder if you might be better off with a vector (better yet, a map) of callback actions and functions.  Something like
using Void_func = void();
using Callback_func = static void(Address, Address);
map<string currency, Void_func> from_button_pressed();
map<string currency, Void_func> to_button_pressed();
map<string currency, Callback_func> cb_from; 
map<string currency, Callback_func> cb_to;
(I'm doing this off the top of my head, so the syntax may be off a little bit.)

Art Werschulz

Osman Zakir

unread,
Sep 1, 2017, 1:10:08 PM9/1/17
to PPP-public
Putting functions into a vector?  Is that a good idea?  I'm not sure.  The parts where each function has to be slightly different than others might be lost.  As for the thing about the empty file, well, I guess I forget to save it.  I'll try again.  Note that right now there's only one conversion rate.  I'm not sure if I did it right.  

And even if I do write it correctly, how should I read it?  Create a conversion_rate type in the program and use it read from the file?  That way I could create a custom istream function that would read in conversion rates the way I want.  Would that be a good idea?
conversion_rates.txt

Art Werschulz

unread,
Sep 1, 2017, 3:33:10 PM9/1/17
to ppp-p...@googlegroups.com
Hi.

> On Sep 1, 2017, at 1:10 PM, Osman Zakir <osman...@gmail.com> wrote:
>
> Putting functions into a vector? Is that a good idea?

It's actually a technique, not a trick. (A technique is a trick that gets used a bunch of times.)

There are even weirder things. Take a look at https://cdecl.org, which translates between C "gibberish" and English. Thus
char (*(*x())[5])()
means
declare x as function returning pointer to array 5 of pointer to function returning char


Art Werschulz
a...@comcast.net



Osman Zakir

unread,
Sep 1, 2017, 6:24:14 PM9/1/17
to PPP-public
When did I ever call it a trick?  I'm just asking if it's a good idea for functions where both the prototypes and definitions are slightly different to each other.  

But right now, I'm asking mainly about the input file.  How to correctly write it and then correctly read it into the program to get the conversions rates I need.   

Osman Zakir

unread,
Sep 1, 2017, 7:41:08 PM9/1/17
to PPP-public
To add to my question about the file: What's the best way to check what the words after the number are?  

Also, is the way I'm reading the number entered into the input textbox into a double fine, or is there a better way?

Thanks in advance for the help.

Christiano

unread,
Sep 1, 2017, 11:13:26 PM9/1/17
to PPP-public
See the image:


The input.txt is:

CHF 1.0365
EUR 1.1860
JPY 0.0091
DEM 0.6064
SGD 0.7369
CAD 0.8068

meaning:

1 CHF = 1.0365 USD

1 EUR = 1.1860 USD

....

1 CAD = 0.8068 USD


The problem itself is very simple, just basic mathematics (output = input*map[currency1]/map[currency2], where map is string to double)

What interested me in this problem was the discussion of Art:
How to write this program without repeating several callbacks and not knowing which currencies the user typed in the input.txt?

The callback format is:
void callback(Address, Address);
where Address is void*

The readers of the book are used to use the second argument, however in this case they can use the first argument which is Fl_Widget* in void* format.
The Fl_Widget* is a pointer to the widget which triggered that callback and have a public method "label()".
That is, we could know what button was clicked using only 1 callback for each them.

See the code bellow:

static void cb_sc1_pressed(Address wid, Address addr) { reference_to<mywindow>(addr).symbol1_choose_pressed( static_cast<Fl_Widget *>(wid)->label()); }

and

void mywindow::symbol1_choose_pressed(string txt)
{
        /* Here you have access to label of the button which triggered this callback */
}


That is, you can create several buttons - with different labels - using the same callback and discover what button was clicked reading "txt" argument. My program above was made using this idea.

Osman Zakir

unread,
Sep 2, 2017, 9:06:22 AM9/2/17
to PPP-public
Ah, so you just read from the file into a std::map?  

So where did you put it have it happen on startup?  And how did you get it the conversion rates into the callback action functions?

Osman Zakir

unread,
Sep 2, 2017, 3:09:11 PM9/2/17
to PPP-public
I'll try out your idea for the callbacks later (maybe), but for now I want to get the conversion rates and everything into the program and make the calculations.  But first I need to find out how to read into a map from a file using an input loop.  If it'll be a good idea in a GUI program (though I'm doing it in a separate public function that'll be called in main().  

This is what I have of that function so far:
void Currency_converter_window::fill_map(std::istream &is)
{
 std
::string rate_id;
 
double rate = 0;
 
for ()
 
{
 
}
 
 
 
const int map_size = 56;
 
for (int i = 0; i < map_size; ++i)
 
{
 
 
}
}

The function is made to work for any kind of istream, but what I'll be passing into it is an ifstream specifically (of course).  

And here's my main():
int main()
{
 
using namespace std;
 
constexpr int win_width = 640, win_height = 440;
 
 
try
 
{
  cout
<< "Enter name of file: ";
 
string file_name;
  cin
>> file_name;
 
Currency_converter_window win{ Point{100, 100}, win_width, win_height, "Currency Converter" };
  ifstream ifs
{ file_name };
  win
.fill_map(ifs);
 
return gui_main();
 
}
 
catch (const runtime_error &e)
 
{
 
Currency_converter_window win{ Point{ 100, 100 }, win_width, win_height, "Currency Converter" };
 
 
Text err_msg_start{ Point{(win_width / 2) - 100, win_height / 2}, "Runtime error: " };
 
Text err_msg{ Point{(win_width / 2) - 100 + err_msg_start.label().length(), win_height / 2}, e.what() };
  err_msg_start
.set_color(Color::black);
  err_msg
.set_color(Color::black);
  win
.attach(err_msg_start);
  win
.attach(err_msg);
 
return gui_main();
 
}
 
catch (const exception &e)
 
{
 
Currency_converter_window win{ Point{ 100, 100 }, win_width, win_height, "Currency Converter" };
 
Text err_msg_start{ Point{ (win_width / 2) - 100, win_height / 2 }, "Exception: " };
 
Text err_msg{ Point{ (win_width / 2) - 100 + err_msg_start.label().length(), win_height / 2 }, e.what() };
  err_msg_start
.set_color(Color::black);
  err_msg
.set_color(Color::black);
  win
.attach(err_msg_start);
  win
.attach(err_msg);
 
return gui_main();
 
}
 
catch (...)
 
{
 
Currency_converter_window win{ Point{ 100, 100 }, win_width, win_height, "Currency Converter" };
 
Text err_msg{ Point{ (win_width / 2) - 100, win_height / 2 }, "An unknown exception occurred" };
  err_msg
.set_color(Color::black);
  win
.attach(err_msg);
 
return gui_main();
 
}
}

I'll try find out how to do it via searching on the Web, but if someone on here can help me out on how to do this, that'll be a great help as well.

Osman Zakir

unread,
Sep 2, 2017, 4:10:03 PM9/2/17
to PPP-public
Newer version of that function's definition:
void Currency_converter_window::fill_map(std::istream &is)
{

 std
::string file_data;
 std
::vector<std::string> rate_ids;
 std
::vector<double> rates;
 
int i = 0;
 
while (getline(is, file_data))
 
{
 
if (file_data[i] != ';'  && isdigit(file_data[i]))

 
{
 
}
 
}
 
 
 
const int map_size = 56;
 
for (int i = 0; i < map_size; ++i)
 
{
 
 
}
}

I'm going to make a file of semicolon-separated key;value pairs and I want to read the numerical values into a double until I encounter a semicolon and the letters and underscores until I encounter a newline into a std::string.  That's the idea.  After that I want to find a way to put those as a key:value pair into a std::map.  This function is for returning the resulting std::map by reference to the calling function.  But the problem is that the function calling it is main().  Will the filled map be accessible to the callbacks and callback actions after this?  What do I do to make sure it is (if anything)?

Osman Zakir

unread,
Sep 2, 2017, 4:31:53 PM9/2/17
to PPP-public
Sorry, yeah, there's no map being passed to it by reference.  Never mind that.  But yeah, that function is a method of the class, so changes made to the map within it should reflect on the map outside it as well, right? 

Osman Zakir

unread,
Sep 3, 2017, 6:46:16 PM9/3/17
to PPP-public
I wrote the code, but when I tested it out, I found that I was weird values in the output textbox.  I ran it in the debugger and found that maybe my fill_map function isn't being called.

Could someone please tell me what the problem might be and how I should fix it?  How do I make sure fill_map() is called before the callbacks are called?  Should I put the map in a separate class so that it's independent of Currency_converter_window objects?  Since right now I can only call after an object of the class has been initialized and at that point it's already too late (since a callback has already been called). 
chapter16ex8.cpp
conversion_rates.txt

Osman Zakir

unread,
Sep 3, 2017, 7:55:18 PM9/3/17
to PPP-public
 Another update.  

I still can't actually get it to fill the map.  What am I missing?

About the callbacks: I think I've figured out at least that.  I'm doing it right now.
chapter16ex8.cpp
conversion_rates.txt

Osman Zakir

unread,
Sep 3, 2017, 8:39:44 PM9/3/17
to PPP-public
This is the new callback I tried to write (tell me what you guys think):

void Currency_converter_window::cb_callback(Address p_widget, Address addr)
{
 
if (std::strncmp(reference_to<Currency_converter_window>(static_cast<Fl_Widget *>(p_widget)).label(), "Convert From", 13) == 0)
 
{
  reference_to
<Currency_converter_window>(addr).from_menu_button_pressed();
 
if (std::strncmp(reference_to<Currency_converter_window>(static_cast<Fl_Widget *>(p_widget)).label(), "USD", 4) == 0)
 
{
   reference_to
<Currency_converter_window>(addr).from_usd_button_pressed();
 
}
 
else if (std::strncmp(reference_to<Currency_converter_window>(static_cast<Fl_Widget *>(p_widget)).label(), "CAD", 4) == 0)
 
{
   reference_to
<Currency_converter_window>(addr).from_cad_button_pressed();
 
}
 
else if (std::strncmp(reference_to<Currency_converter_window>(static_cast<Fl_Widget *>(p_widget)).label(), "AUD", 4) == 0)
 
{
   reference_to
<Currency_converter_window>(addr).from_aud_button_pressed();
 
}
 
else if (std::strncmp(reference_to<Currency_converter_window>(static_cast<Fl_Widget *>(p_widget)).label(), "EUR", 4) == 0)
 
{
   reference_to
<Currency_converter_window>(addr).from_eur_button_pressed();
 
}
 
else if (std::strncmp(reference_to<Currency_converter_window>(static_cast<Fl_Widget *>(p_widget)).label(), "CNY", 4) == 0)
 
{
   reference_to
<Currency_converter_window>(addr).from_cny_button_pressed();
 
}
 
else if (std::strncmp(reference_to<Currency_converter_window>(static_cast<Fl_Widget *>(p_widget)).label(), "JPY", 4) == 0)
 
{
   reference_to
<Currency_converter_window>(addr).from_jpy_button_pressed();
 
}
 
else if (std::strncmp(reference_to<Currency_converter_window>(static_cast<Fl_Widget *>(p_widget)).label(), "DKK", 4) == 0)
 
{
   reference_to
<Currency_converter_window>(addr).from_dkk_button_pressed();
 
}
 
else if (std::strncmp(reference_to<Currency_converter_window>(static_cast<Fl_Widget *>(p_widget)).label(), "EGP", 4) == 0)
 
{
   reference_to
<Currency_converter_window>(addr).from_egp_button_pressed();
 
}
 
}
 
else if (std::strncmp(reference_to<Currency_converter_window>(static_cast<Fl_Widget *>(p_widget)).label(), "Convert To", 11) == 0)
 
{
  reference_to
<Currency_converter_window>(addr).to_menu_button_pressed();
 
if (std::strncmp(reference_to<Currency_converter_window>(static_cast<Fl_Widget *>(p_widget)).label(), "USD", 4) == 0)
 
{
   reference_to
<Currency_converter_window>(addr).to_usd_button_pressed();
 
}
 
else if (std::strncmp(reference_to<Currency_converter_window>(static_cast<Fl_Widget *>(p_widget)).label(), "CAD", 4) == 0)
 
{
   reference_to
<Currency_converter_window>(addr).to_cad_button_pressed();
 
}
 
else if (std::strncmp(reference_to<Currency_converter_window>(static_cast<Fl_Widget *>(p_widget)).label(), "AUD", 4) == 0)
 
{
   reference_to
<Currency_converter_window>(addr).to_aud_button_pressed();
 
}
 
else if (std::strncmp(reference_to<Currency_converter_window>(static_cast<Fl_Widget *>(p_widget)).label(), "EUR", 4) == 0)
 
{
   reference_to
<Currency_converter_window>(addr).to_eur_button_pressed();
 
}
 
else if (std::strncmp(reference_to<Currency_converter_window>(static_cast<Fl_Widget *>(p_widget)).label(), "CNY", 4) == 0)
 
{
   reference_to
<Currency_converter_window>(addr).to_cny_button_pressed();
 
}
 
else if (std::strncmp(reference_to<Currency_converter_window>(static_cast<Fl_Widget *>(p_widget)).label(), "JPY", 4) == 0)
 
{
   reference_to
<Currency_converter_window>(addr).to_jpy_button_pressed();
 
}
 
else if (std::strncmp(reference_to<Currency_converter_window>(static_cast<Fl_Widget *>(p_widget)).label(), "DKK", 4) == 0)
 
{
   reference_to
<Currency_converter_window>(addr).to_dkk_button_pressed();
 
}
 
else if (std::strncmp(reference_to<Currency_converter_window>(static_cast<Fl_Widget *>(p_widget)).label(), "EGP", 4) == 0)
 
{
   reference_to
<Currency_converter_window>(addr).to_egp_button_pressed();
 
}
 
}
 
else if (std::strncmp(reference_to<Currency_converter_window>(static_cast<Fl_Widget *>(p_widget)).label(), "OK", 3) == 0)
 
{
  reference_to
<Currency_converter_window>(addr).ok_button_pressed();
 
}
}

Osman Zakir

unread,
Sep 4, 2017, 9:26:27 AM9/4/17
to PPP-public
Okay, another update. 

I made some more changes, but I still can't get the std::map to be filled.  What am I doing wrong here?  Please help.  With the single callback that should check for the label on the buttons as well.  Thanks in advance.
chapter16ex8.cpp
conversion_rates.txt

Osman Zakir

unread,
Sep 4, 2017, 5:55:06 PM9/4/17
to PPP-public
I'm almost done now, I think.  I figured how to get the map filled (thank you, Stack Oveflow (though I just read through a thread that was already rather than ask my own question)), and now I just need to know how to get the single callback working right (no matter what I do, the output I get when I used that callback is "0"), and also how to fix the output that I get that's currently truncating the fractional part.   

Thanks in advance (and would somebody please reply? I don't making multiple consecutive posts one after another like this (if only there were an "edit" button here).  
chapter16ex8.cpp
conversion_rates.txt

Osman Zakir

unread,
Sep 4, 2017, 9:07:09 PM9/4/17
to PPP-public
Alright, now I just need to know about that all-purpose callback.  For the problem of the output being truncated, I defined an Output::put(double) function:

void Out_box::put(double d)
{
 
Fl_Output& po = reference_to<Fl_Output>(pw);
 std
::stringstream ss;
 ss
<< d;
 po
.value(ss.str().c_str());
}

I put that function in GUI.h (prototype) and GUI.cpp (definition).  Now the output comes out as a floating-point value like it should when I do "result.put(converted_amt);".  I also made an In_box::get_double() function, just in case, though I might be a bit overkill (and I don't need it for this exercise, either):

double In_box::get_double()
{
 
Fl_Input& pi = reference_to<Fl_Input>(pw);
 
const char* p = pi.value();
 
if (!isdigit(p[0])) return -999999;
 
return static_cast<double>(atof(p));
}

Now the only problem I have with my solution for this exercise is that I don't know how to proceed normally after catching exceptions.  Like, if I forget to input an original amount to convert from and just manipulate the menus and press OK, I end up catching an std::exception with a message saying that the std::stod() function didn't get a valid argument.  If I try to do a conversion after that without closing and starting the program again, I get a garbage value as my output.  I don't know what to do to fix this.  

Osman Zakir

unread,
Sep 5, 2017, 4:50:45 PM9/5/17
to PPP-public
Another update.  I just need to see how to fix the all-purpose callback now.  Everything else is good.  As for what I did, you can read the code to see it.
chapter16ex8.cpp
Reply all
Reply to author
Forward
0 new messages