
I looked through boost and if there is a library or facility for data binding in boost I could not find it... So I created and am submitting a simple example for review. Please take a look and give some feedback. I understand that there is quiet a process of review before a library is included in boost. If there is interest or if there is already on going work on the concept than I am interested in helping. Here goes: Data binding should allow for 1 value of primitive type to be changed in code and have 1 to N values be modified based on the event and optional conversion functions. i.e. fanout should be allowed Data binding should allow for cascading changes. Where a->b->c->d where -> represents a binding link when 'a' changes 'c', 'b', and 'd' should change. Data binding should be recursion save where a->b->c->a where a change in 'a' causes a change in 'c', but will not cause a recursive change in 'a' creating a recursive loop. Data binding would be useful in the dataflow library and for work being done on cppgui or for that fact any UI development. This is my first submittal to this list so please bear with me. I have taken the liberty to add the boost license to this code... so it is boost's if the project so chooses to incorporate or enhance the concept. I am not the originator of data binding concept and make no claim to this. I am only providing a simple example for discussion and hopefull inclusion of data binding concept in boost and ....ultimately modify the C++ compiler to provide for a lighter weight solution (events based on modifications of integral types) - Ouch ... yes I know what I am asking here... see the last part of the example output for why I ask this. // Start snip bind_value.hpp ======================================= // // (C) Copyright Brian Davis. // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // // Revision history: // 19 July 2008 // Created - to support and assist in dataflow and dataflow concepts // // This is not currently part of boost! The only reason for the above copyright is to // keep these concepts part of the boost development. boost::data_binding is not // officially part of boost. The goal of this demo code is to elaborate on data binding // and give a simple implementation and an example on how the concept can be applied within C++. #ifndef BOOST_DATA_BINDING_HPP #include <boost/cast.hpp> #include <boost/function.hpp> #include <boost/bind.hpp> #include <boost/foreach.hpp> #include <vector> namespace boost { namespace data_binding { template < class T1, class T2> class bind_values; template <class T> class value { friend class bind_values< class T1, class T2>; public: typedef boost::function< T& (T&) > set_value_function; std::vector<set_value_function> set_value_functions; T& mv_value; // To prevent infinite recursion on a group of three linked variables a->b->c->a. bool mv_entered; protected: private: public: value( T& invalue ) : mv_value( invalue ){ mv_entered = false; }; // Sets require action event handling to update value of binded type.. gets are transparent value& operator=( T& rhs ) { if( set_value_functions.size() && !mv_entered ) { mv_entered = true; BOOST_FOREACH( set_value_function set_value, set_value_functions ) { mv_value = set_value( rhs ); } } else { // Otherwise simple type cast convert mv_value = boost::numeric_cast<T>( rhs ); } mv_entered = false; return *this; } value& operator=( T rhs ) { if( set_value_functions.size() && !mv_entered ) { mv_entered = true; BOOST_FOREACH( set_value_function set_value, set_value_functions ) { mv_value = set_value( rhs ); } } else { // Otherwise simple type cast convert mv_value = boost::numeric_cast<T>( rhs ); } mv_entered = false; return *this; } void add_set_function( set_value_function set_function ) { set_value_functions.push_back( set_function ); } T& operator&( value<T>& value ) { return value.mv_value; } protected: private: }; template < class T1, class T2> class bind_values { friend class value<T1>; friend class value<T2>; public: typedef T1 type1; typedef T2 type2; typedef boost::function< T1& (T1, T2)> convert_to_function; typedef boost::function< T2& (T2, T1)> convert_from_function; convert_to_function convert_to; convert_from_function convert_from; protected: value<T1>& mv_value1; value<T2>& mv_value2; private: public: bind_values ( value<T1>& value1, value<T2>& value2, convert_to_function convertTo, convert_from_function convertFrom ) : convert_to(convertTo), convert_from(convertFrom), mv_value1( value1 ), mv_value2( value2 ) { // Hook up set for value 1 mv_value1.add_set_function( boost::bind( &bind_values::set_value1, this, _1 ) ); // Hook up set for value 2 mv_value2.add_set_function( boost::bind( &bind_values::set_value2, this, _1 ) ); } bind_values( value<T1>& value1, value<T2>& value2 ) : mv_value1( value1 ), mv_value2( value2 ) { // Hook up set for value 1 mv_value1.set_value = boost::bind( &bind_values::set_value1, _1 ); // Hook up set for value 2 mv_value2.set_value = boost::bind( &bind_values::set_value2, _1 ); } protected: T1& set_value1( T1& value ) { if( convert_from ) { // Convert and set value 2 mv_value2 = boost::numeric_cast<T2>( convert_from( mv_value2.mv_value, value ) ); } else { // Otherwise simple type cast convert mv_value2 = boost::numeric_cast<T2>( value ); } // return the value unmodified to the original container return value; } T2& set_value2( T2& value ) { if( convert_to ) { // Convert and set value 1 mv_value1 = boost::numeric_cast<T1>( convert_to( mv_value1.mv_value, value ) ); } else { // Otherwise simple type cast convert mv_value1 = boost::numeric_cast<T1>( value ); } // return the value unmodified to the original container return value; } private: }; } } // namespace boost::data_binding template <class T> std::ostream& operator<<( std::ostream& os, boost::data_binding::value<T>& rhs ) { os << rhs.mv_value;; return os; } #endif // end snip bind_value.hpp ======================================= // Start snip bind_value_test.hpp ======================================= // // (C) Copyright Brian Davis. // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // // Revision history: // 19 July 2008 // Created - to support and assist in dataflow and dataflow concepts // // This is not currently part of boost! The only reason for the above copyright is to // keep these concepts part of the boost development. boost::data_binding is not // officially part of boost. The goal of this demo code is to elaborate on data binding // and give a simple implementation and an example on how the concept can be applied within C++. #include <iostream> #include <boost/cstdint.hpp> #include <boost/bind.hpp> #include <boost/data_binding/bind_value.hpp> #define MAX_CELSIUS 100 #define MIN_CELSIUS -17.8 #define MAX_RAW_SENSOR_COUNTS 4095 // use of a class is not necessary it is only shown to prove it is possible. It may // make more sense just to have conversion functions class AClass_That_Is_A_Converter { public: protected: boost::data_binding::value<boost::uint32_t> rawCelsiusCountsFromSensor; boost::data_binding::value<float> calibratedCelsiusDataFromSensor; boost::data_binding::value<float> calibratedFahrenheit; boost::uint32_t rawSensorValue; float fahrenheit; float celsius; boost::data_binding::bind_values<float, boost::uint32_t> mv_celsiusToSensorBinder; boost::data_binding::bind_values<float, float> mv_celsiusToFahrenheitBinder; private: public: AClass_That_Is_A_Converter( void ) : rawCelsiusCountsFromSensor(rawSensorValue), calibratedCelsiusDataFromSensor(celsius), calibratedFahrenheit(fahrenheit), mv_celsiusToSensorBinder ( calibratedCelsiusDataFromSensor, rawCelsiusCountsFromSensor, boost::bind( &AClass_That_Is_A_Converter::convertCelsiusRawToCalibrated, this, _1, _2 ), boost::bind( &AClass_That_Is_A_Converter::convertCelsiusCalibratedToRaw, this, _1, _2 ) ), mv_celsiusToFahrenheitBinder ( calibratedCelsiusDataFromSensor, calibratedFahrenheit, boost::bind( &AClass_That_Is_A_Converter::convertFahrenheitToCelsius, this, _1, _2 ), boost::bind( &AClass_That_Is_A_Converter::convertCelsiusToFahrenheit, this, _1, _2 ) ) {} virtual ~AClass_That_Is_A_Converter( void ){} void test( void ) { std::cout << "Starting test of data binding" << std::endl; try { std::cout<< "Note raw temp represents the raw value of an Analog to Digital Converter" << std::endl; std::cout<< "The range is from 0-4095 which represents -17.8 - 100 degrees on the Celsius" << std::endl; std::cout<< "scale and 0 - 212 degrees on the Fahrenheit" << std::endl; std::cout<< "Setting calibratedFahrenheit to 70.0 degrees fahrenheit" << std::endl; std::cout<< "calibratedFahrenheit = 70.0f;" << std::endl; calibratedFahrenheit = 70.0f; std::cout << "What does the celsius value contain? ... should be ~21.1" << std::endl; showValues(); std::cout << "Now change the raw sensor value and see what happens to Celsius and Fahrenheit" << std::endl; std::cout<< "rawCelsiusCountsFromSensor = 4095;" << std::endl; rawCelsiusCountsFromSensor = 4095; std::cout << "Celsius should equal ~100" << std::endl; std::cout << "Fahrenheit should equal ~212" << std::endl; showValues(); std::cout << "Now change celsius see what happens to raw and farenheit" << std::endl; std::cout<< "calibratedCelsiusDataFromSensor = 32.2;" << std::endl; calibratedCelsiusDataFromSensor = 32.2; std::cout << "Fahrenheit should equal ~90 degress" << std::endl; showValues(); std::cout << "Note changing original values will not have any affect in C++ " << std::endl; std::cout << "this makes me sad :-( as a class wrapper and operator overloading" << std::endl; std::cout << "is necessary... without changing the compiler... hint.... hint..." << std::endl; std::cout << "fahrenheit = 10;" << std::endl; fahrenheit = 10; showValues(); std::cout << "No change in values :-( !!!" << std::endl; // Note one to many fanout is possible and the example shows 1 to 2 fanout with Celsius // fanning out to Raw and Fahrenheit values. A more advanced implementation is also possible // which includes the use of the lamda library and place holders. This is just a simple // example which I hope will "prime the pump" for work on a data_binding library with in // the Boost development. Most direct use for this library is within data_flow library. // Another use is within the cppgui library which would allow modification of data to directly // affect values of UI controls and display values. } catch( std::exception& ex ) { std::cout << "Failed with exception: " << ex.what(); } std::cout << "End of data binding test" << std::endl; } void showValues( void ) { std::cout << std::endl; std::cout << "Fahrenheit : " << calibratedFahrenheit << std::endl; std::cout << "Celsius : " << calibratedCelsiusDataFromSensor << std::endl; std::cout << "Raw Temp To/From Sensor A/D converter : " << rawCelsiusCountsFromSensor << std::endl; std::cout << std::endl; std::cout << "fahrenheit : " << fahrenheit << std::endl; std::cout << "celsius : " << celsius << std::endl; std::cout << "rawSensorValue : " << rawSensorValue << std::endl; std::cout << std::endl; std::cout << "====================================================================" << std::endl; } protected: float& convertFahrenheitToCelsius ( float& celsius , float& fahrenheit ) { return celsius = ((fahrenheit - 32.0) * 5.0) / 9.0; } float& convertCelsiusToFahrenheit ( float& fahrenheit, float& celsius ) { return fahrenheit = celsius * (9.0 / 5.0) + 32.0; } float& convertCelsiusRawToCalibrated ( float& calibratedValue, boost::uint32_t& rawValue ) { return calibratedValue = ( MAX_CELSIUS - MIN_CELSIUS ) * (rawValue / MAX_RAW_SENSOR_COUNTS) + MIN_CELSIUS; } boost::uint32_t& convertCelsiusCalibratedToRaw ( boost::uint32_t& rawValue, float& calibratedValue ) { // Raw is from 0 to 4095 ( a 12 bit number of an // Analog to Digital converter // map zero (fahrenheit) to water boiling to raw value of 4095 return rawValue = MAX_RAW_SENSOR_COUNTS * ( calibratedValue - MIN_CELSIUS) / ( MAX_CELSIUS - MIN_CELSIUS ); } private: }; int main(int argc, char **argv) { AClass_That_Is_A_Converter anExampleOfDataBinding; anExampleOfDataBinding.test(); } // end snip bind_value_test.hpp ======================================= // Start snip program output ======================================= Starting test of data binding Note raw temp represents the raw value of an Analog to Digital Converter The range is from 0-4095 which represents -17.8 - 100 degrees on the Celsius scale and 0 - 212 degrees on the Fahrenheit Setting calibratedFahrenheit to 70.0 degrees fahrenheit calibratedFahrenheit = 70.0f; What does the celsius value contain? ... should be ~21.1 Fahrenheit : 70 Celsius : 21.1111 Raw Temp To/From Sensor A/D converter : 1352 fahrenheit : 70 celsius : 21.1111 rawSensorValue : 1352 ==================================================================== Now change the raw sensor value and see what happens to Celsius and Fahrenheit rawCelsiusCountsFromSensor = 4095; Celsius should equal ~100 Fahrenheit should equal ~212 Fahrenheit : 212 Celsius : 100 Raw Temp To/From Sensor A/D converter : 4095 fahrenheit : 212 celsius : 100 rawSensorValue : 4095 ==================================================================== Now change celsius see what happens to raw and farenheit calibratedCelsiusDataFromSensor = 32.2; Fahrenheit should equal ~90 degress Fahrenheit : 89.96 Celsius : 32.2 Raw Temp To/From Sensor A/D converter : 1738 fahrenheit : 89.96 celsius : 32.2 rawSensorValue : 1738 ==================================================================== Note changing original values will not have any affect in C++ this makes me sad :-( as a class wrapper and operator overloading is necessary... without changing the compiler... hint.... hint... fahrenheit = 10; Fahrenheit : 10 Celsius : 32.2 Raw Temp To/From Sensor A/D converter : 1738 fahrenheit : 10 celsius : 32.2 rawSensorValue : 1738 ==================================================================== No change in values :-( !!! End of data binding test // End snip program output ======================================= This was done to assist Sjepan Rajko in his work on the dataflow library (he beat me to it :-) ). Dataflow provides the event model data_binding provides the means to do data conversion between events. Brian.