Fugui
Fugui

Reputation: 91

Is this pass by value or pass by reference?

I'm using cplex with C++. Recently, I was looking at the following example. Then I'm confused by the populate function. It is pass-by-value right? But why did the model created in main function updated after executing populate function? Shouldn't it still be empty model?

// -------------------------------------------------------------- -*- C++ -*-
// File: ilolpex1.cpp
// Version 12.5
// --------------------------------------------------------------------------
// Licensed Materials - Property of IBM
// 5725-A06 5725-A29 5724-Y48 5724-Y49 5724-Y54 5724-Y55 5655-Y21
// Copyright IBM Corporation 2000, 2012. All Rights Reserved.
//
// US Government Users Restricted Rights - Use, duplication or
// disclosure restricted by GSA ADP Schedule Contract with
// IBM Corp.
// --------------------------------------------------------------------------
//
// ilolpex1.cpp - Entering and optimizing a problem.  Demonstrates different
// methods for creating a problem.  The user has to choose the method
// on the command line:
//
//    ilolpex1  -r     generates the problem by adding rows
//    ilolpex1  -c     generates the problem by adding columns
//    ilolpex1  -n     generates the problem by adding a list of coefficients

#include <ilcplex/ilocplex.h>
ILOSTLBEGIN

static void
   usage (const char *progname),
   populatebyrow     (IloModel model, IloNumVarArray var, IloRangeArray con),
   populatebycolumn  (IloModel model, IloNumVarArray var, IloRangeArray con),
   populatebynonzero (IloModel model, IloNumVarArray var, IloRangeArray con);

int
main (int argc, char **argv)
{
   IloEnv   env;
   try {
      IloModel model(env);

      if (( argc != 2 )                         ||
          ( argv[1][0] != '-' )                 ||
          ( strchr ("rcn", argv[1][1]) == NULL )   ) {
         usage (argv[0]);
         throw(-1);
      }

      IloNumVarArray var(env);
      IloRangeArray con(env);

      switch (argv[1][1]) {
         case 'r':
            populatebyrow (model, var, con);
            break;
         case 'c':
            populatebycolumn (model, var, con);
            break;
         case 'n':
            populatebynonzero (model, var, con);
            break;
      }

      IloCplex cplex(model);
      cplex.exportModel("lpex1.lp");

      // Optimize the problem and obtain solution.
      if ( !cplex.solve() ) {
         env.error() << "Failed to optimize LP" << endl;
         throw(-1);
      }

      IloNumArray vals(env);
      env.out() << "Solution status = " << cplex.getStatus() << endl;
      env.out() << "Solution value  = " << cplex.getObjValue() << endl;
      cplex.getValues(vals, var);
      env.out() << "Values        = " << vals << endl;
      cplex.getSlacks(vals, con);
      env.out() << "Slacks        = " << vals << endl;
      cplex.getDuals(vals, con);
      env.out() << "Duals         = " << vals << endl;
      cplex.getReducedCosts(vals, var);
      env.out() << "Reduced Costs = " << vals << endl;
   }
   catch (IloException& e) {
      cerr << "Concert exception caught: " << e << endl;
   }
   catch (...) {
      cerr << "Unknown exception caught" << endl;
   }

   env.end();

   return 0;
}  // END main


static void usage (const char *progname)
{
   cerr << "Usage: " << progname << " -X" << endl;
   cerr << "   where X is one of the following options:" << endl;
   cerr << "      r          generate problem by row" << endl;
   cerr << "      c          generate problem by column" << endl;
   cerr << "      n          generate problem by nonzero" << endl;
   cerr << " Exiting..." << endl;
} // END usage


// To populate by row, we first create the variables, and then use them to
// create the range constraints and objective.

static void
populatebyrow (IloModel model, IloNumVarArray x, IloRangeArray c)
{
   IloEnv env = model.getEnv();

   x.add(IloNumVar(env, 0.0, 40.0));
   x.add(IloNumVar(env));
   x.add(IloNumVar(env));

   model.add(IloMaximize(env, x[0] + 2 * x[1] + 3 * x[2]));

   c.add( - x[0] +     x[1] + x[2] <= 20);
   c.add(   x[0] - 3 * x[1] + x[2] <= 30);

   x[0].setName("x1");
   x[1].setName("x2");
   x[2].setName("x3");

   c[0].setName("c1");
   c[1].setName("c2");
   model.add(c);

}  // END populatebyrow


// To populate by column, we first create the range constraints and the
// objective, and then create the variables and add them to the ranges and
// objective using column expressions.

static void
populatebycolumn (IloModel model, IloNumVarArray x, IloRangeArray c)
{
   IloEnv env = model.getEnv();

   IloObjective obj = IloMaximize(env);
   c.add(IloRange(env, -IloInfinity, 20.0, "c1"));
   c.add(IloRange(env, -IloInfinity, 30.0, "c2"));

   x.add(IloNumVar(obj(1.0) + c[0](-1.0) + c[1]( 1.0), 0.0, 40.0));
   x.add(IloNumVar(obj(2.0) + c[0]( 1.0) + c[1](-3.0)));
   x.add(IloNumVar(obj(3.0) + c[0]( 1.0) + c[1]( 1.0)));

   x[0].setName("x1");
   x[1].setName("x2");
   x[2].setName("x3");

   model.add(obj);
   model.add(c);

}  // END populatebycolumn


// To populate by nonzero, we first create the rows, then create the
// columns, and then change the nonzeros of the matrix 1 at a time.

static void
populatebynonzero (IloModel model, IloNumVarArray x, IloRangeArray c)
{
   IloEnv env = model.getEnv();

   IloObjective obj = IloMaximize(env);
   c.add(IloRange(env, -IloInfinity, 20.0));
   c.add(IloRange(env, -IloInfinity, 30.0));

   x.add(IloNumVar(env, 0.0, 40.0));
   x.add(IloNumVar(env));
   x.add(IloNumVar(env));

   obj.setLinearCoef(x[0], 1.0);
   obj.setLinearCoef(x[1], 2.0);
   obj.setLinearCoef(x[2], 3.0);

   c[0].setLinearCoef(x[0], -1.0);
   c[0].setLinearCoef(x[1],  1.0);
   c[0].setLinearCoef(x[2],  1.0);
   c[1].setLinearCoef(x[0],  1.0);
   c[1].setLinearCoef(x[1], -3.0);
   c[1].setLinearCoef(x[2],  1.0);

   c[0].setName("c1");
   c[1].setName("c2");

   x[0].setName("x1");
   x[1].setName("x2");
   x[2].setName("x3");

   model.add(obj);
   model.add(c);
}  // END populatebynonzero

Upvotes: 0

Views: 656

Answers (3)

TimChippingtonDerrick
TimChippingtonDerrick

Reputation: 2072

Yup, these classes are really "handle" classes - really just some syntactic sugar wrapped around a good old pointer. The reasons are that it is easier to make the code look cleaner that way, and it allows the library to maintain better control over what is going on than just allowing pointers everywhere, and makes it easier to do stuff like making sure objects get allocated on a custom heap for example, to get better performance through using specialised memory management that knows a bit more about the types of objects it must manage. I've been using CPLEX for many projects since the late 90s and taught many courses with it too. If done right it can make the code much cleaner. Just when you implement your own custom extensions you sometimes need to write both the implementation class and a handle class, which can be confusing to start with...

Upvotes: 1

Kun Ling
Kun Ling

Reputation: 2219

Please check:

  1. Whether your header file have customized copy constructor?

  2. Is the class IloModel is a type based on pointer? Like exebook have mentioned?

detail explaination of the customized copy constructor

whether your code have a customized copy constructor, especially when the class have some data field create in heap. Please check your code for the definition of class IloModel.

  • If no customized copy construction, the model class should be passed by value.

  • If a customized copy constructor is defined, and only reference is used, pass by reference is possible.

Here is a piece of code which could show you how this type of constructor works:

#include <string>
#include <iostream>

using namespace std;

class person {
  public:
  std::string _name;
  int* _age_ptr; 

  person (const std::string& name, int age) {
    _name = name;
    _age_ptr = new int(age); 
  }

  person(const person& that) {  // The customized copy constructor
    _name = that._name;
    _age_ptr = that._age_ptr;
    cout << "run copy constructor" << endl;
  }

  void print() {
    cout << "name:" << _name <<", age:" << *_age_ptr << endl;
  }

};

person update_age( person that) {
  cout << "Set age tp 70" << endl;
  *that._age_ptr = 70;
  return that;
}

int main() {
  person a("Jim", 60);
  cout <<"Before update, a" << endl;
  a.print(); // Output: name:Jim, age 60
  cout << endl;
  person b = update_age(a);
  cout <<"After update, a" << endl;
  a.print(); // HERE Output: name:Jim, age 70
  cout << endl;
  cout <<"After update, b" << endl;                                                                       
  b.print();
  return 0;
}

For more information about copy constructor, here is a useful link: What is the copy-and-swap idiom?

Upvotes: -1

exebook
exebook

Reputation: 33900

See the ilocplex.h, probably IloModel is defined as pointer. Or "a handle" as the manual states.

I cannot find ilocplex.h easily using google. But that's how I suppose it works:

struct ModelObj {
   int data1, data2, data3;
}

struct Model {
   ModelObj* obj;
}

Now you can pass Model as value, but still obj will point to the reference.

Upvotes: 1

Related Questions