Hyrum K. Wright <hyrum_wright_at_mail.utexas.edu <mailto:hyrum_wright_at_mail.utexas.edu?Subject=Re:%20object-model:%20Wrapping%20Subversion%20C-structs%20in%20C%2B%2B>> wrote:

For the C++ folks out there, I've got a question about an approach to
take on the object-model branch. At issue is how to wrap the various
C structures returned to callers, particularly in a backward
compatible manner. Currently, I'm looking at svn_wc_notify_t *. As I
see it, there are a few options:

1) Just wrap the pointer to the C struct as a member of the wrapper class.
    Pros: Easy to implement; lightweight constructor.
    Cons: Getters would need to translate to C++ types; would need to
implement a copy constructor which deep copies the C struct; would
also introduce pools, since creating and duplicating C structs
requires them.

2) Wrap each C struct member individually
    Pros: C->C++ complexity is constrained to the constructor,
everything else is C++ types
    Cons: Hard to extend for future compatibility

3) Just pass the C-struct pointer around; don't even bother with a class
    Pros: Dead simple.
    Cons: Requires more memory management thought by consumers; not
C++-y enough; may introduce wrapping difficulties.

I'd like to come up with something consistent, which would be used
throughout the C++ bindings. I'm also interested in a solution which
ensures the C++ bindings can be used as the basis for other
object-oriented bindings models (Python, Perl, etc.)

Thoughts?

One issue that has not been talked about in this thread
is strong typing. If you remember the problems with
Johan's diff / blame optimizations, the reason behind
it was a confusion of type semantics. Some ints were
line numbers, others were file offsets. But there was / is
no formal way to tell them apart.

Since you decided to use templates in your code, I
thought I would give it a try and design a simple template
class that allows you to define any number of int-like
types that are mutually distinct and require explicit
conversion.

It would be nice to have the C++ wrappers use these
types instead of plain ints etc. in their signatures.

-- Stefan^2.

// TypedInts.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

// extend that enum to define further types / kinds of integers

enum IntegerTypes
{
    itLineNumber,
    itFileOffset,
    itRevision
};

// a type selection utility struct
//   (X, Y, true)  -> X
//   (X, Y, false) -> Y

template<class First, class Second, bool get_first>
struct SelectType
{
};

template<class First, class Second>
struct SelectType<First, Second, true>
{
    typedef typename First type;
};

template<class First, class Second>
struct SelectType<First, Second, false>
{
    typedef typename Second type;
};

// a utility struct mapping a (potentially unsigned) integer 
// to the corresponding signed integer.

template<class T>
struct DiffType
{
    typedef typename T type;
};

template<>
struct DiffType<unsigned char>
{
    typedef char type;
};

template<>
struct DiffType<unsigned short>
{
    typedef short type;
};

template<>
struct DiffType<unsigned>
{
    typedef int type;
};

template<>
struct DiffType<unsigned long>
{
    typedef long type;
};

template<>
struct DiffType<unsigned long long>
{
    typedef long long type;
};

// A typed integer:
//   V .. base integer type (e.g. unsigned)
//   T .. formal classification, i.e. this actually separates the int types
//   diff_type .. if true, this is the difference type
//                if false, this is the absolute value type
//                TypedInt(X,Y,false) - TypedInt(X,Y,false) -> 
TypedInt(X,Y,true)
//
// The arithmetics, conversions and getters have been carefully
// designed that only meaningful combinations of arguments are
// valid and everything else will be rejected by the compiler.
//
// In optimized code, this class does not impose any runtime overhead 
// over plain use of built-in types.

template<class V, IntegerTypes T, bool diff_type>
class TypedInt
{
public:

    // encapsulated int types

    typedef typename V absolute_value_t;
    typedef typename DiffType<V>::type diff_value_t;
    typedef typename SelectType<diff_value_t, 
                                absolute_value_t, 
                                diff_type>::type value_t;

    // typed integers

    typedef typename TypedInt<V, T, false> absolute_t;
    typedef typename TypedInt<V, T, true> diff_t;
    typedef typename TypedInt<V, T, diff_type> this_t;

    // expose template parameters

    enum {type = T, is_diff = diff_type};

    // construction, auto-conversion

    TypedInt()
        : value()
    {
    }

    TypedInt(value_t value)
        : value(value)
    {
    }

    // data access

    value_t& get()
    {
        return value;
    }

    const value_t& get() const
    {
        return value;
    }

    value_t* operator&()
    {
        return &value;
    }

    const value_t* operator&() const
    {
        return &value;
    }

    // assignment

    TypedInt& operator=(value_t rhs)
    {
        value = rhs;
        return *this;
    }

    TypedInt& operator=(const TypedInt& rhs)
    {
        value = rhs.value;
        return *this;
    }

    // comparison

    bool operator!() const
    {
        return !value;
    }

    bool operator==(const TypedInt& rhs)
    {
        return value == rhs.value;
    }
    bool operator!=(const TypedInt& rhs)
    {
        return value != rhs.value;
    }

    bool operator<(const TypedInt& rhs)
    {
        return value < rhs.value;
    }
    bool operator<=(const TypedInt& rhs)
    {
        return value <= rhs.value;
    }

    bool operator>(const TypedInt& rhs)
    {
        return value > rhs.value;
    }
    bool operator>=(const TypedInt& rhs)
    {
        return value >= rhs.value;
    }

    // arithmetics: increment / decrement

    TypedInt& operator++()
    {
        ++value;
        return *this;
    }
    TypedInt operator++(int)
    {
        return TypedInt(value++);
    }

    TypedInt& operator--()
    {
        --value;
        return *this;
    }
    TypedInt operator--(int)
    {
        return TypedInt(value--);
    }

    // arithmetics: prefix

    const TypedInt& operator+()
    {
        return *this;
    }

    diff_t operator-()
    {
        return diff_t(static_cast<diff_value_t>(-value));
    }

    // modifying additive / differental operations:
    // +=   abs  += diff
    //      diff += diff
    // -=   abs  -= diff
    //      diff -= diff

    TypedInt& operator+=(const diff_t& rhs)
    {
        value += static_cast<value_t>(rhs.get());
        return *this;
    }

    TypedInt& operator-=(const diff_t& rhs)
    {
        value -= static_cast<value_t>(rhs.get());
        return *this;
    }

    // arithmetics: scaling

    TypedInt& operator*=(value_t rhs)
    {
        value *= rhs;
        return *this;
    }
    TypedInt operator*(value_t rhs) const
    {
        return TypedInt(value * value);
    }

    TypedInt& operator/=(value_t rhs)
    {
        value /= value;
        return *this;
    }
    TypedInt operator/(value_t rhs) const
    {
        return TypedInt(value / value);
    }

    TypedInt& operator%=(value_t rhs)
    {
        value %= value;
        return *this;
    }
    TypedInt operator%(value_t rhs) const
    {
        return TypedInt(value % value);
    }

private:

    value_t value;
};

// additive / differental operations:
// +    abs  = abs  + diff
//      abs  = diff + abs
//      diff = diff + diff
// -    abs  = abs  - diff
//      diff = abs  - abs
//      diff = diff - diff

template<class V, IntegerTypes T, bool diff_type>
inline TypedInt<V, T, diff_type>
operator+(const TypedInt<V, T, true>& lhs, const TypedInt<V, T, diff_type>& rhs)
{
    typedef typename TypedInt<V, T, diff_type> result_t;
    typedef typename result_t::value_t value_t;

    return result_t (static_cast<value_t>(lhs.get()) + rhs.get());
}

template<class V, IntegerTypes T>
inline TypedInt<V, T, false>
operator+(const TypedInt<V, T, false>& lhs, const TypedInt<V, T, true>& rhs)
{
    typedef typename TypedInt<V, T, false> result_t;
    typedef typename result_t::value_t value_t;

    return result_t (static_cast<value_t>(lhs.get()) + rhs.get());
}

template<class V, IntegerTypes T, bool diff_type>
inline TypedInt<V, T, !diff_type>
operator-(const TypedInt<V, T, false>& lhs, const TypedInt<V, T, diff_type>& 
rhs)
{
    typedef typename TypedInt<V, T, !diff_type> result_t;
    typedef typename result_t::value_t value_t;

    return result_t (static_cast<value_t>(lhs.get()) - rhs.get());
}

template<class V, IntegerTypes T>
inline TypedInt<V, T, true>
operator-(const TypedInt<V, T, true>& lhs, const TypedInt<V, T, true>& rhs)
{
    typedef typename TypedInt<V, T, true> result_t;
    typedef typename result_t::value_t value_t;

    return result_t (lhs.get() - static_cast<value_t>(rhs.get()));
}

// some applications of the typed ints

typedef TypedInt<int, itLineNumber, false> LineNumber;
typedef TypedInt<int, itLineNumber, true> LineDiff;
typedef TypedInt<int, itRevision, false> RevisionNumber;
typedef TypedInt<int, itRevision, true> RevisionDiff;

// some test code:

int _tmain(int argc, _TCHAR* argv[])
{
    RevisionNumber r0, r1(argc);
    LineNumber l0(argc), l1(20);

    for (RevisionNumber r = r0; r < r1; ++r)
    {
        if (l0 > l1)
            l0 += 2;
        else
            l1 *= 3;

        // The following statements (until the end of the block) are 
        // ineffective because they add zeros etc. Thus, they will be 
        // removed by the optimizer.

        // does not compile (since these ints are different):
        // r0 = l0;
        // r0 += l0;
        // r0 = l1 - l0;

        LineDiff ld1 = l1 - l0;
        LineNumber l2 = l1 - ld1;
        LineDiff ld3 = ld1 - ld1;

        l1 = l1 + ld3;
        l0 = ld3 + l0;
        ld3 = ld3 + ld3;

        // does not compile (since diff - abs and abs + abs are not defined):
        // l1 = ld1 - l1;
        // ld3 = l1 + l1;

        // does not compile (since diffs are different types than absolutes):
        // l1 = ld3;
        // ld3 = l1;
    }

    return l1.get();
}

// generated code:
/*
  00000 55               push    ebp
  00001 8b ec            mov     ebp, esp

; 317  :     RevisionNumber r0, r1(argc);
; 318  :     LineNumber l0(argc), l1(20);

  00003 8b 4d 08         mov     ecx, DWORD PTR _argc$[ebp]
  00006 8b d1            mov     edx, ecx
  00008 b8 14 00 00 00   mov     eax, 20                        ; 00000014H

; 319  : 
; 320  :     for (RevisionNumber r = r0; r < r1; ++r)

  0000d 85 c9            test    ecx, ecx
  0000f 7e 0f            jle     SHORT $...@wmain
$l...@wmain:

; 321  :     {
; 322  :         if (l0 > l1)

  00011 3b d0            cmp     edx, eax
  00013 7e 05            jle     SHORT $...@wmain

; 323  :             l0 += 2;

  00015 83 c2 02         add     edx, 2

; 324  :         else

  00018 eb 03            jmp     SHORT $l...@wmain
$...@wmain:

; 325  :             l1 *= 3;

  0001a 8d 04 40         lea     eax, DWORD PTR [eax+eax*2]
$l...@wmain:

; 319  : 
; 320  :     for (RevisionNumber r = r0; r < r1; ++r)

  0001d 49               dec     ecx
  0001e 75 f1            jne     SHORT $l...@wmain
$...@wmain:

  00020 5d               pop     ebp
  00021 c3               ret     0
*/

Reply via email to