# C++ Notes: FLTK Example - Body Mass Index

Here's a sample FLTK example. Building the GUI takes place in `main`. The `cb_compute` function is a callback function which is called when the user clicks on the button. It's quite easy to get and set input and output field values, but the amount of conversion and error checking using string streams and exceptions obscures this simplicity a bit.

### Source file for the GUI interface

      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    // fltk-bmi-exceptions/bmi.cpp - Compute Body Mass Index w/(h*h)
    // This is written in "procedural style" instead of OO,

    // 2004-02-12 - Fred Swartz

    //===================================================== includes
    #include <FL/Fl.H>
    #include <FL/Fl_Window.H>
    #include <FL/Fl_Button.H>
    #include <FL/Fl_Input.H>
    #include <FL/Fl_Output.H>
    #include <iostream>
    #include <iomanip>
    #include <sstream>
    #include <stdexcept>
    #include "bmi.h"            // computeBMI
    using namespace std;

    //===================================================== prototypes
    void cb_compute( Fl_Widget* widget, void* data);

    //===================================================== globals
    // These widgets are declared global so that main can
    // create them and put them in the window, and so that
    // the callback function can also reference them.
    Fl_Input*  input_height;
    Fl_Input*  input_weight;
    Fl_Output* output_bmi;

    //===================================================== main
    int main() {
        Fl_Button* button_compute;

        //-- Create window and add widgets to it.
        Fl_Window* win= new Fl_Window(300,200, "Body Mass Index Calculator");
        win->begin();
            input_height  = new Fl_Input( 120,  10, 140, 30, "Height in inches");
            input_weight  = new Fl_Input( 120,  60, 140, 30, "Weight in pounds");
            button_compute= new Fl_Button(100, 110, 120, 30, "Compute BMI");
            output_bmi    = new Fl_Output(120, 160, 140, 30, "Body Mass Index");
        win->end();

        //-- Connect button widget to its callback function
        button_compute->callback( (Fl_Callback*) cb_compute );

        win->show();      // Make the window visible

        return Fl::run(); // Turn control over to FLTK
    }

    //====================================================== cb_compute
       // This callback function for the button converts the
       // input fields to floats, converts those values to
       // metric and calls the function to compute BMI.
    void cb_compute( Fl_Widget* widget , void* data) {
        //-- Convert input field values, compute BMI, display
        static const float METERS_PER_INCH = 0.0254;
        static const float KILOGRAMS_PER_POUNDS = 0.454;

        //-- Convert the values from the input fields from
        //   const char* to floats using string streams.
        float heightInches;
        istringstream inchStream(input_height->value());
        if (!(inchStream >> heightInches)) {
            output_bmi->value("Bad Height");
            return;
        }
        //-- This checks for extra values by trying to read a character
        char c;
        if (inchStream >> c) {
            output_bmi->value("Bad Height format");
            return;
        }

        //-- This checks for extra values in input by skipping whitespace (ws)
        //   and then seeing if we're not at EOF, which we should be.
        float weightPounds;
        istringstream poundStream(input_weight->value());
        if (!(poundStream >> weightPounds >> ws) || !(poundStream.eof())) {
            output_bmi->value("Bad Weight");
            return;
        }

        try {
            //-- Compute the Body Mass index.
            float heightM  = METERS_PER_INCH * heightInches;
            float weightKg = KILOGRAMS_PER_POUNDS * weightPounds;
            float bmi = computeBMI(heightM, weightKg);

            //-- Convert bmi to char and set output field.
            ostringstream bmiStream;
            bmiStream << fixed << setprecision(1) << bmi;
            output_bmi->value(bmiStream.str().c_str());

        } catch (out_of_range oorex) {
            // computeBMI can throw an out_of_range exception.
            output_bmi->value(oorex.what());
        }
    }

### The BMI logic header file

      1
      2
      3
      4
      5
    // fltk-1/bmi.h -- Body Mass Index
    #ifndef BMI_H
    #define BMI_H
    float computeBMI(float heightInMeters, float weightInKg);
    #endif

### The BMI logic source

      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
    // fltk-1/bmi-logic.cpp - Logic which computes Body Mass Index.
    // 2004-02-10 - Fred Swartz
    // Throws out_of_range if parameters aren't "reasonable".

    // The following function is the entire "logic" of the problem.
    // It could just as easily be called from a command line interface.

    #include <stdexcept>
    #include "bmi.h"

    //====================================================== computeBMI
    float computeBMI(float heightInMeters, float weightInKg) {
        if (heightInMeters < 1.0 || heightInMeters > 3.0) {
            throw std::out_of_range("Height out of range");
        } else if (weightInKg < 20.0 || weightInKg > 200) {
            throw std::out_of_range("Weight out of range");
        }
        return weightInKg / (heightInMeters*heightInMeters);
    }