ContentsIndexHome
PreviousUpNext
C++ callback Example

The Error callback example in C++.

The LoggingCallback and the DebuggingCallback are both implementations of the ICallBack. 

The LoggingCallback logs the minor errors in stead of throwing them, the DebuggingCallback asserts false if an InvalidUse error is encountered. 

The CallBack run function makes some calls on the SDK which will cause errors, to show how the implementations of the ICallBack will react.

  1: #include "aimms/Include.h"
  2: #include "aimms/AIMMS.h"
  3: #include <assert.h>
  4: 
  5: // This example demonstrates the custom handling of errors.
  6: // It overrides the default behavior in which aimms::exceptions are thrown in all error cases in two ways:
  7: // The LoggingCallback logs all minor errors, and keeps throwing the severe ones
  8: // The DebuggingCallback logs as well, and asserts where errors can only be expected if the model was called erroneously.
  9: 
 10: // NB: to run with the DebuggingCallback, change the type of Callback in main
 11: 
 12: class LoggingCallback : public aimms::ICallBack
 13: {
 14:     virtual void onMessages(std::vector<aimms::AimmsMessage>& messages )
 15:     {
 16:         /* Overridden behavior:
 17:         Only throw if an error is caused by the communication layer or the AIMMS model. 
 18:         Send message to std::cout instead in other cases
 19:         */
 20: 
 21:         for( size_t m = 0; m < messages.size(); ++m){
 22:             switch(messages[m].code)
 23:             {
 24:             case aimms::AM_Info:
 25:                 std::cout<<  "info: "<< messages[m].message << std::endl;
 26:                 break;
 27:             case aimms::AM_Warning: 
 28:                 std::cout<< "warning: "<< messages[m].message << std::endl;
 29:                 break;
 30:             case aimms::AM_InvalidUse: 
 31:                 std::cout<<  "invalid use: "<< messages[m].message<< std::endl;
 32:                 break;
 33:             case aimms::AM_Runtime: 
 34:                 std::cout<< "runtime error: "<< messages[m].message<< std::endl;
 35:                 break;
 36:             case aimms::AM_License : 
 37:                 throw aimms::LicenseException(messages[m].message.c_str());
 38:             case aimms::AM_Communication: 
 39:                 throw aimms::CommunicationException(messages[m].message.c_str());
 40:             case aimms::AM_General:  // AIMMS Model errors
 41:                 throw aimms::Exception(messages[m].code, messages[0].message.c_str());
 42:             default: 
 43:                 throw aimms::Exception(messages[m].code, messages[0].message.c_str());
 44:             }
 45: 
 46:             /* 
 47:             The rationale behind this distinction is that in the cases in which now exceptions are thrown, 
 48:             the state of the data is not known. For example: if during the flush of the buffers an error on 
 49:             a specific value is detected by the AIMMS model, it is not known which part of the remaining data 
 50:             is already written to the model. 
 51:             */
 52: 
 53:         }
 54:     }
 55: };
 56: 
 57: class DebuggingCallback : public aimms::ICallBack
 58: {
 59:     virtual void onMessages(std::vector<aimms::AimmsMessage>& messages )
 60:     {
 61:         /* Overridden behavior:
 62: 
 63:         assert if an AM_InvalidUse is encountered, because that is an indication of a bug. 
 64:         For example: adding a string to a numerical parameter gives AM_InvalidUse. 
 65:         */
 66: 
 67:         for( size_t m = 0; m < messages.size(); ++m){
 68:             switch(messages[m].code)
 69:             {
 70:             case aimms::AM_InvalidUse: 
 71:                 std::cout<<  "invalid use: "<< messages[m].message<< std::endl;
 72:                 assert(false);
 73:                 break;
 74:             default:
 75:                 std::cout<<  messages[m].code << ":" << messages[m].message << std::endl;
 76:                 break;
 77:             }
 78: 
 79:         }
 80:     }
 81: 
 82: };
 83: 
 84: 
 85: int main(int argc, const char* argv[]) 
 86: {
 87:     if (argc != 3) {
 88:         std::cerr << "Invalid number of arguments. usage: <location of AIMMS>  <location of project>" << std::endl; 
 89:         return 1;
 90:     }  
 91:     aimms::ISession* session = 0;
 92:     try {
 93:         session = aimms::openSession(argv[1],argv[2]);
 94:     } catch (std::exception & e) {
 95:         std::cerr << e.what();
 96:         return 1;
 97:     }
 98: 
 99:     LoggingCallback cb;
100:     //Attach the custom Callback to the session.
101:     session->setCallBack(&cb);
102: 
103:     //To illustrate the behavior of our callback, make some mistakes:
104: 
105:     //Try to retrieve the set Locations as a multidimensional parameter.
106:     //This is an InvalidUse error: 
107:     //  LoggingCallback will not throw
108:     //  DebuggingCallback will assert here. 
109:     aimms::IMultiDimData* parameterLocations = session->openMultiDim("Locations");
110: 
111:     if(parameterLocations != 0){
112:         std::cout << "Expected an InvalidUse error!" << std::endl;;
113:     }
114: 
115:     aimms::IMultiDimData* parameterSupply = session->openMultiDim("Supply");
116:     //Do not forget to check the result if the callback does not throw on each error!
117:     if (parameterSupply == 0){
118:         std::cout << "Did not expect an error here!" << std::endl;
119:     } else{
120:         // Since we did not add any depots, we will get a runtime error here:
121:         parameterSupply->setValue(aimms::Tuple("London"), 20.0);
122: 
123:         // Using "insert" will resolve this error, since it adds London to the index domain of Supply if not existing. 
124:         parameterSupply->insert(aimms::Tuple("London"), 20.0);
125: 
126:         // Supply is a numeric parameter: it will not allow strings as value:
127:         // DebuggingCallback will assert.
128:         parameterSupply->insert(aimms::Tuple("Paris"), "thirty");
129:     }
130: 
131:     aimms::IMultiDimData* parameterDemand = session->openMultiDim("Demand");
132: 
133:     if (parameterDemand == 0){
134:         std::cout << "Did not expect an error here!" << std::endl;
135:     }  else{
136:         aimms::ITuple* tup = parameterDemand->createTuple();
137: 
138: 
139:         // "London" exists in the set Locations (because we added it to Depots by inserting in Supply),
140:         // but the index domain of Demand is Customers, which is still empty.
141:         // The nocheck_ functions are less expensive because they do not check this in advance.
142:         // Cout will not show an error after these actions:
143:         tup->getElement(0)->nocheck_setLabel("London");
144:         parameterDemand->setValue(tup, 90);
145:         tup->close();
146: 
147:         // But this performance improvement comes with a downside:
148:         try{
149:             // The mistake will become apparent when the data is sent to the model. 
150:             // Then it is undetermined wether the inserted value for "London" in Supply is indeed inserted in Aimms.
151:             // If Londen is added to the domainset and this set is flushed before the Supply is flushed,
152:             // the Supply of Londen will be set to its new value without an error.
153:             // If Londen is not added or Supply is flushed earlier (which may be due to an internal flush),
154:             // then an error will occur and even other buffered modifications to Supply may not have been inserted in Aimms.
155:             session->updateData();
156: 
157:         } catch (aimms::Exception & e){
158:             std::cerr << e.what();
159:         }
160: 
161:         parameterSupply->close();
162:         parameterDemand->close();
163:         session->close();
164:     }
165: 
166: }