tags:

views:

110

answers:

2

I'm currently designing a public-facing C++ API for a product which will require a precompiled binary/DLL (it will be cross-platform). I'd 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'm 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've done it (with respect to IloInt, IloNum, IloAny, Ilo*Var, etc., see link [1] below (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'm 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: Okay, well as a first step I've decided to stick an 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;
+2  A: 

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.

Mads Elvheim
I am using Boost in the implementation, but would rather avoid including a dependency to Boost in the public (read: user) API.
Geoff
A: 

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