2

I am currently designing a public-facing C++ API for a product which will require a pre-compiled binary/DLL (it will be cross-platform). I would like for the API to allow the user to use any POD we support (where applicable), however the base requirements are maximum flexibility and binary compatibility. I am doing something a bit similar to CPLEX's API (it's one of several inspirations), but I'm thinking there's possibly a better way of specifying type information than how they have done it (with respect to IloInt, IloNum, IloAny, Ilo*Var, etc., see link (hopefully) for IloExtractable branch) without messing with binary compatibility. Am I wrong? I've got something in mind, but I can't recall what it is or if it'll even work, I believe it resembles something like the Visitor or Decorator patterns but for types, could someone please enlighten me on the subject? I've got my Design Patterns book by the GoF out in front of me.

Note: any syntax errors there might be here aren't part of the problem at hand.

Examples of what I believe I can't use, and the reason:

Possibly.. but this is likely to make things complicated with the Expression tree.

template<typename ValueType>
Constraint : public Expression;

Probably will impact future expansion.

IntConstraint : public Expression;
LongConstraint : public Expression;
DoubleConstraint : public Expression;

Ugly as sin, may cause plenty of subtle issues.

union Value
{
 int AsInt,
 float AsFloat
};
class Constraint : public Expression
{
  public:
  Value GetValue(Edge);
  void SetValue(Value, Edge);
  void SetUpper(Value, Vertex);
  void SetLower(Value, Vertex);
  ...
};

Edit: In response to Mads Elvheim (and after finding this link) I now realize that I don't need to exclude templates from my possibilities which is good, but I am still rather unsure it's the best idea - at least for the Constraints class (even though it's conceptually sound), forgive me for not being as clear as I thought.

In order to make my API easy to use I defined the grammar using bnf (which is rather new to me). This led to an abstract syntax tree for Expression, Constraints, and other classes which will be included which interact with Constraints. Because other classes will be interacting with Constraints, I would prefer avoiding passing as munch type information as possible until "the last minute" so to speak.. I feel like I may be missing a level of abstraction.

Studying CPLEX gives me the impression that they modeled their types by following the domains of numbers (whole and real), linear equation expression (of course) and what should accordingly be possible, which absolutely makes sense, hmm...

(Apparently I can't post more than one link as I'm a new user.)

Edit 2: As a first step I've decided to stick a ConstraintExpressionArgument in between the Constraint and Expression classes so I can still identify a constraint in my expression tree without being aware of the type it manipulates which is good.

Another detail I may have neglected to mention was that unlike in CPLEX where the Constraint class is unusable by itself, my Constraint class is currently a usable user class but like in CPLEX I also want to leave room for expansion (thus the typing issue)...

Anyway, at the moment I have the equivalent of

class ConstraintExpressionArgument : public Expression;
template<typename ValueType>
class Constraint : public ConstraintExpressionArgument;
Nathan Smith
  • 683
  • 1
  • 10
  • 24
Geoff
  • 474
  • 3
  • 14
  • let me paraphrase.. You want to allow users to set contraints, but allow the values passed to the constraints to be any POD such as int or float? Are they able to mix & match these contraints at the same time? The set of POD data-types is pretty fixed.. Do you really need extensibility in that direction? – sean riley Oct 14 '09 at 00:35
  • Right, and yes they can mix and match. I could simply offer only long values as cplex does with IloInt, but because floating point can't be excluded from user choice I'm looking for the most seamless way of providing the option of using multiple types. – Geoff Oct 14 '09 at 16:31
  • link [1] is here: http://www.mat.uc.pt/~jsoares/teaching/software/cplex/doc/html/refconcert/html/graph.html – Geoff Oct 14 '09 at 18:33

2 Answers2

2

Most systems provide a <types.h> or <inttypes.h> header with type definitions you can use, if the exact range or number of bits is important. I don't see a good reason to throw inheritance at the problem. You can also use std::numeric_limits together with Boost's BOOST_STATIC_ASSERT() macro to generate meaningful compile-time asserts, if a specified type or template type doesn't match a requirement. The requirement could be integer vs float, range, minimum representable value, accuracy, etc. For example:

#include <limits>
#include <inttypes.h>
#include <boost/static_assert.hpp>

template<class T, int bits> T Add(const T& a, const T& b)
{
    BOOST_STATIC_ASSERT( std::numeric_limits<T>::is_integer     );
    BOOST_STATIC_ASSERT( std::numeric_limits<T>::is_signed      );
    BOOST_STATIC_ASSERT( std::numeric_limits<T>::digits == bits );

    return a + b;
}

If std::numeric_limits doesn't have all of your types, use template specialization to extend and implement them.

  • I am using Boost in the implementation, but would rather avoid including a dependency to Boost in the public (read: user) API. – Geoff Oct 14 '09 at 22:32
0

Right, I think I've got what I need, at least for now.

The above mentioned

class ConstraintExpressionArgument : public Expression;
template<typename ValueType>
class Constraint : public ConstraintExpressionArgument;

got me on the right track to separating type from constraint-ness.

Geoff
  • 474
  • 3
  • 14