which is better?

Discussion in 'C++' started by wogston, Sep 26, 2003.

  1. wogston

    wogston Guest

    (A)

    template <typename T>
    metainline foo<T> lerp(const foo<T>& a, const foo<T>& b, const T& time)
    {
    foo<T> v;
    repeat< 4,exec_mul<T> >::exec(v,a,b,time);
    return v;
    }

    (B)

    template <typename T>
    inline foo<T> lerp(const foo<T>& a, const foo<T>& b, const T& time)
    {
    return foo<T>(
    a[0] + (b[0] - a[0]) * time,
    a[1] + (b[1] - a[1]) * time,
    a[2] + (b[2] - a[2]) * time,
    a[3] + (b[3] - a[3]) * time);
    }

    Which of the above: (A) or (B) is "better" from the viewpoint of the
    language police? I am mainly interested in efficiency (assuming correctness
    is met in both cases). I'm unrolling repeatitive tasks in (A), but I would
    like to know if this is the famous NRVO I keep hearing about ; the return
    value is named.. but is (B) theoretically more efficient, excluding possible
    overhead from repeat<> template?

    Here's the repeat and exec_mul in case they are relevant:

    template <typename SCALAR>
    struct exec_lerp
    {
    template <typename A, typename B, typename C>
    static metainline void exec(int index, A& a, const B& b, const C& c,
    const SCALAR& d)
    {
    a[index] = b[index] + (c[index] - b[index]) * d;
    }
    };

    template <int SIZE, typename E>
    struct repeat
    {
    enum { INDEX = SIZE - 1 };

    template <typename A, typename B, typename C>
    static metainline void exec(A& a, const B& b, const C& c)
    {
    repeat<INDEX,E>::exec(a,b,c);
    E::exec(INDEX,a,b,c);
    }

    template <typename A, typename B>
    static metainline void exec(A& a, const B& b)
    {
    repeat<INDEX,E>::exec(a,b);
    E::exec(INDEX,a,b);
    }

    template <typename A>
    static metainline void exec(A& a)
    {
    repeat<INDEX,E>::exec(a);
    E::exec(INDEX,a);
    }
    };

    In otherwords, is this case of Named Return Value Optimization? While I am
    aware that C++ doesn't recognize such concept, but contemporary compilers do
    and this isn't std.c++ but rather generic C++ group I thought to ask here to
    learn more: I'm interested in performance (and correctness). The "foo" type
    has const and non-const [] operator, through which it accesses the
    components, the internal presentation is static array, in most cases like
    this:

    template <typename SCALAR, int SIZE>
    class foo
    {
    protected:
    SCALAR m_v[SIZE];
    // ...
    };

    Ie. is it generally (with current compilers!) prefered to return-by-value
    constructing the return value object in return statement, or give it a name
    and return?

    I posted on same topic few years back on my opinions on my current vector
    library, where I named the members:

    SCALAR x,y,z,w; // example

    These are possible to initialize in constructor using initializers, array
    isn't.. but array was recommended back then so I decided to give it a shot
    in the current design, so from this point of view it doesn't make any
    difference if I try to initialize the object in constructor or just unroll
    and initialize components with a template.

    Any thoughts? Criticism? Helpful suggestions?


    --
    "I seen things you couldn't imagine.." - B.B. Warner
    "I seen you do the them and it wasn't pretty.." - Anonymous
    wogston, Sep 26, 2003
    #1
    1. Advertising

  2. On Sat, 27 Sep 2003 01:53:38 +0300, "wogston" <> wrote:
    >
    >(A)
    >
    >template <typename T>
    >metainline foo<T> lerp(const foo<T>& a, const foo<T>& b, const T& time)


    C++ has no keyword 'metainline', and you don't define a 'metainline'
    macro in the code presented here.

    Right here you're already in off-topic non-C++ land.



    >{
    > foo<T> v;
    > repeat< 4,exec_mul<T> >::exec(v,a,b,time);
    > return v;
    >}
    >
    >(B)
    >
    >template <typename T>
    >inline foo<T> lerp(const foo<T>& a, const foo<T>& b, const T& time)
    >{
    > return foo<T>(
    > a[0] + (b[0] - a[0]) * time,
    > a[1] + (b[1] - a[1]) * time,
    > a[2] + (b[2] - a[2]) * time,
    > a[3] + (b[3] - a[3]) * time);
    >}
    >
    >Which of the above: (A) or (B) is "better"


    Asking for "better" is trolling.



    >from the viewpoint of the language police?


    Using deragatory terms like "language police" is trolling.



    >I am mainly interested in efficiency (assuming correctness
    >is met in both cases). I'm unrolling repeatitive tasks in (A), but I would
    >like to know if this is the famous NRVO I keep hearing about ;


    It is not.


    >the return value is named..


    It is not.



    > but is (B) theoretically more efficient, excluding possible
    >overhead from repeat<> template?


    That is a Quality Of Implementation issue (also known as trolling).

    Assuming the compiler does not optimize, the version with a single
    'return'-statement should generally be more efficient than the one
    with additional statements.

    There is no answer without such assumptions.



    >Here's the repeat and exec_mul in case they are relevant:
    >
    >template <typename SCALAR>
    >struct exec_lerp
    >{
    > template <typename A, typename B, typename C>
    > static metainline void exec(int index, A& a, const B& b, const C& c,


    As mentioned, C++ has no keyword 'metainline'.



    >const SCALAR& d)
    > {
    > a[index] = b[index] + (c[index] - b[index]) * d;
    > }
    >};
    >
    >template <int SIZE, typename E>
    >struct repeat
    >{
    > enum { INDEX = SIZE - 1 };
    >
    > template <typename A, typename B, typename C>
    > static metainline void exec(A& a, const B& b, const C& c)
    > {
    > repeat<INDEX,E>::exec(a,b,c);
    > E::exec(INDEX,a,b,c);
    > }
    >
    > template <typename A, typename B>
    > static metainline void exec(A& a, const B& b)
    > {
    > repeat<INDEX,E>::exec(a,b);
    > E::exec(INDEX,a,b);
    > }
    >
    > template <typename A>
    > static metainline void exec(A& a)
    > {
    > repeat<INDEX,E>::exec(a);
    > E::exec(INDEX,a);
    > }
    >};
    >
    >In otherwords, is this case of Named Return Value Optimization?


    No (NRVO is a language extension that you don't use here).


    > While I am
    >aware that C++ doesn't recognize such concept, but contemporary compilers do
    >and this isn't std.c++ but rather generic C++ group


    That is incorrect.

    Read Shiva's welcome-text.

    Read the FAQ.



    > I thought to ask here to
    >learn more: I'm interested in performance (and correctness). The "foo" type
    >has const and non-const [] operator, through which it accesses the
    >components, the internal presentation is static array, in most cases like
    >this:
    >
    >template <typename SCALAR, int SIZE>
    >class foo
    >{
    >protected:
    > SCALAR m_v[SIZE];
    >// ...
    >};


    Why don't you use a std::vector<>?



    >Ie. is it generally (with current compilers!) prefered to return-by-value
    >constructing the return value object in return statement, or give it a name
    >and return?


    Whatever gives clear, readable code.



    >I posted on same topic few years back on my opinions on my current vector
    >library, where I named the members:
    >
    >SCALAR x,y,z,w; // example
    >
    >These are possible to initialize in constructor using initializers, array
    >isn't.. but array was recommended back then so I decided to give it a shot
    >in the current design, so from this point of view it doesn't make any
    >difference if I try to initialize the object in constructor or just unroll
    >and initialize components with a template.
    >
    >Any thoughts? Criticism? Helpful suggestions?


    Try to describe what you're trying to achieve, not your technical solution.
    Alf P. Steinbach, Sep 27, 2003
    #2
    1. Advertising

  3. wogston

    wogston Guest

    > C++ has no keyword 'metainline', and you don't define a 'metainline'
    > macro in the code presented here.
    >
    > Right here you're already in off-topic non-C++ land.


    My bad, it was copy-paste from "real code", not typed specificly to here.
    It's a macro for __forceinline or inline, depending on what compiler you
    happen to be on. OK, so __forceinline is off-topic here, assume it reads
    "inline" from this point forward.
    wogston, Sep 27, 2003
    #3
  4. wogston

    wogston Guest

    > > C++ has no keyword 'metainline', and you don't define a 'metainline'
    > > macro in the code presented here.


    Here are the full definitions. hope this helps (somehow I doubt it, I tried
    to trim it down to minimum previous time). If there are further headers you
    would like to see before staying on-topic let me know. ;-)



    // begin "configure.hpp"

    #ifndef PRMATH_CONFIGURE_HPP
    #define PRMATH_CONFIGURE_HPP
    namespace prmath
    {
    // ------------------------------------------------------------
    // Microsoft Visual C++
    // ------------------------------------------------------------
    #if defined(__VISUALC__)
    #pragma inline_depth(255)
    #pragma inline_recursion(on)
    #pragma auto_inline(on)
    #ifndef metainline
    #define metainline __forceinline
    #endif
    #ifndef PRMATH_EXPRESSION_ENABLE
    #define PRMATH_EXPRESSION_ENABLE
    #endif
    #endif
    // ------------------------------------------------------------
    // Intel C++
    // ------------------------------------------------------------
    #if defined(__INTEL_COMPILER)
    #ifndef metainline
    #define metainline __forceinline
    #endif
    #endif
    // ------------------------------------------------------------
    // generic c++ compiler
    // ------------------------------------------------------------
    #ifndef metainline
    #define metainline inline
    #endif
    #ifdef PRMATH_EXPRESSION_DISABLE
    #ifdef PRMATH_EXPRESSION_ENABLE
    #undef PRMATH_EXPRESSION_ENABLE
    #endif
    #endif
    #ifdef PRMATH_TYPENAME_DISABLE
    #ifdef PRMATH_TYPENAME_ENABLE
    #undef PRMATH_TYPENAME_ENABLE
    #endif
    #else
    #ifndef PRMATH_TYPENAME_ENABLE
    #define PRMATH_TYPENAME_ENABLE
    #endif
    #endif
    } // namespace prmath
    #endif


    // begin "evaluate.hpp"

    #ifndef PRMATH_EVALUATE_HPP
    #define PRMATH_EVALUATE_HPP
    #include <cassert>
    #include "configure.hpp"
    namespace prmath
    {
    // ------------------------------------------------------------
    // permute masks
    // ------------------------------------------------------------
    enum
    {
    xxx = 0, yxx = 1, zxx = 2,
    xyx = 4, yyx = 5, zyx = 6,
    xzx = 8, yzx = 9, zzx = 10,
    xxy = 16, yxy = 17, zxy = 18,
    xyy = 20, yyy = 21, zyy = 22,
    xzy = 24, yzy = 25, zzy = 26,
    xxz = 32, yxz = 33, zxz = 34,
    xyz = 36, yyz = 37, zyz = 38,
    xzz = 40, yzz = 41, zzz = 42
    };
    enum
    {
    xxxx,yxxx,zxxx,wxxx,xyxx,yyxx,zyxx,wyxx,xzxx,yzxx,zzxx,wzxx,xwxx,ywxx,zwxx,w
    wxx,
    xxyx,yxyx,zxyx,wxyx,xyyx,yyyx,zyyx,wyyx,xzyx,yzyx,zzyx,wzyx,xwyx,ywyx,zwyx,w
    wyx,
    xxzx,yxzx,zxzx,wxzx,xyzx,yyzx,zyzx,wyzx,xzzx,yzzx,zzzx,wzzx,xwzx,ywzx,zwzx,w
    wzx,
    xxwx,yxwx,zxwx,wxwx,xywx,yywx,zywx,wywx,xzwx,yzwx,zzwx,wzwx,xwwx,ywwx,zwwx,w
    wwx,
    xxxy,yxxy,zxxy,wxxy,xyxy,yyxy,zyxy,wyxy,xzxy,yzxy,zzxy,wzxy,xwxy,ywxy,zwxy,w
    wxy,
    xxyy,yxyy,zxyy,wxyy,xyyy,yyyy,zyyy,wyyy,xzyy,yzyy,zzyy,wzyy,xwyy,ywyy,zwyy,w
    wyy,
    xxzy,yxzy,zxzy,wxzy,xyzy,yyzy,zyzy,wyzy,xzzy,yzzy,zzzy,wzzy,xwzy,ywzy,zwzy,w
    wzy,
    xxwy,yxwy,zxwy,wxwy,xywy,yywy,zywy,wywy,xzwy,yzwy,zzwy,wzwy,xwwy,ywwy,zwwy,w
    wwy,
    xxxz,yxxz,zxxz,wxxz,xyxz,yyxz,zyxz,wyxz,xzxz,yzxz,zzxz,wzxz,xwxz,ywxz,zwxz,w
    wxz,
    xxyz,yxyz,zxyz,wxyz,xyyz,yyyz,zyyz,wyyz,xzyz,yzyz,zzyz,wzyz,xwyz,ywyz,zwyz,w
    wyz,
    xxzz,yxzz,zxzz,wxzz,xyzz,yyzz,zyzz,wyzz,xzzz,yzzz,zzzz,wzzz,xwzz,ywzz,zwzz,w
    wzz,
    xxwz,yxwz,zxwz,wxwz,xywz,yywz,zywz,wywz,xzwz,yzwz,zzwz,wzwz,xwwz,ywwz,zwwz,w
    wwz,
    xxxw,yxxw,zxxw,wxxw,xyxw,yyxw,zyxw,wyxw,xzxw,yzxw,zzxw,wzxw,xwxw,ywxw,zwxw,w
    wxw,
    xxyw,yxyw,zxyw,wxyw,xyyw,yyyw,zyyw,wyyw,xzyw,yzyw,zzyw,wzyw,xwyw,ywyw,zwyw,w
    wyw,
    xxzw,yxzw,zxzw,wxzw,xyzw,yyzw,zyzw,wyzw,xzzw,yzzw,zzzw,wzzw,xwzw,ywzw,zwzw,w
    wzw,
    xxww,yxww,zxww,wxww,xyww,yyww,zyww,wyww,xzww,yzww,zzww,wzww,xwww,ywww,zwww,w
    www
    };
    // ------------------------------------------------------------
    // execs
    // ------------------------------------------------------------
    template <typename SCALAR>
    struct exec_copy
    {
    template <typename A, typename B>
    static metainline void exec(int index, A& a, const B& b)
    {
    a[index] = b[index];
    }
    template <typename A>
    static metainline void exec(int index, A& a, const SCALAR& b)
    {
    a[index] = b;
    }
    };
    template <typename SCALAR>
    struct exec_min
    {
    template <typename A, typename B, typename C>
    static metainline void exec(int index, A& a, const B& b, const C& c)
    {
    a[index] = b[index] < c[index] ? b[index] : c[index];
    }
    template <typename A, typename B>
    static metainline void exec(int index, A& a, const B& b)
    {
    a[index] = a[index] < b[index] ? a[index] : b[index];
    }
    };
    template <typename SCALAR>
    struct exec_max
    {
    template <typename A, typename B, typename C>
    static metainline void exec(int index, A& a, const B& b, const C& c)
    {
    a[index] = b[index] > c[index] ? b[index] : c[index];
    }
    template <typename A, typename B>
    static metainline void exec(int index, A& a, const B& b)
    {
    a[index] = a[index] > b[index] ? a[index] : b[index];
    }
    };
    template <typename SCALAR>
    struct exec_neg
    {
    template <typename A, typename B>
    static metainline void exec(int index, A& a, const B& b)
    {
    a[index] = -b[index];
    }
    template <typename A>
    static metainline void exec(int index, A& a)
    {
    a[index] = -a[index];
    }
    };
    template <typename SCALAR>
    struct exec_add
    {
    template <typename A, typename B, typename C>
    static metainline void exec(int index, A& a, const B& b, const C& c)
    {
    a[index] = b[index] + c[index];
    }
    template <typename A, typename B>
    static metainline void exec(int index, A& a, const B& b)
    {
    a[index] += b[index];
    }
    };
    template <typename SCALAR>
    struct exec_sub
    {
    template <typename A, typename B, typename C>
    static metainline void exec(int index, A& a, const B& b, const C& c)
    {
    a[index] = b[index] - c[index];
    }
    template <typename A, typename B>
    static metainline void exec(int index, A& a, const B& b)
    {
    a[index] -= b[index];
    }
    };
    template <typename SCALAR>
    struct exec_mul
    {
    template <typename A, typename B, typename C>
    static metainline void exec(int index, A& a, const B& b, const C& c)
    {
    a[index] = b[index] * c[index];
    }
    template <typename A, typename B>
    static metainline void exec(int index, A& a, const B& b)
    {
    a[index] *= b[index];
    }
    template <typename A, typename B>
    static metainline void exec(int index, A& a, const B& b, const SCALAR& c)
    {
    a[index] = b[index] * c;
    }
    template <typename A>
    static metainline void exec(int index, A& a, const SCALAR& b)
    {
    a[index] *= b;
    }
    };
    template <typename SCALAR, int SIZE>
    struct exec_cross
    {
    template <typename A, typename B, typename C>
    static metainline void exec(int, A&, const B&, const C&)
    {
    }
    };
    template <typename SCALAR>
    struct exec_cross<SCALAR,3>
    {
    template <typename A, typename B, typename C>
    static metainline void exec(int index, A& a, const B& b, const C& c)
    {
    switch ( index )
    {
    case 0: a[0] = b[1] * c[2] - b[2] * c[1]; break;
    case 1: a[1] = b[2] * c[0] - b[0] * c[2]; break;
    case 2: a[2] = b[0] * c[1] - b[1] * c[0]; break;
    }
    }
    };
    template <typename SCALAR>
    struct exec_lerp
    {
    template <typename A, typename B, typename C>
    static metainline void exec(int index, A& a, const B& b, const C& c, const
    SCALAR& d)
    {
    a[index] = b[index] + (c[index] - b[index]) * d;
    }
    };
    template <typename SCALAR>
    struct exec_permute
    {
    template <typename A, typename B>
    static metainline void exec(int index, A& a, const B& b, const int& mask)
    {
    const int idx = (mask >> (index * 2)) & 0x03;
    a[index] = b[idx];
    }
    };
    // ------------------------------------------------------------
    // repeat
    // ------------------------------------------------------------
    template <int SIZE, typename E>
    struct repeat
    {
    enum { INDEX = SIZE - 1 };
    template <typename A, typename B, typename C>
    static metainline void exec(A& a, const B& b, const C& c)
    {
    repeat<INDEX,E>::exec(a,b,c);
    E::exec(INDEX,a,b,c);
    }
    template <typename A, typename B>
    static metainline void exec(A& a, const B& b)
    {
    repeat<INDEX,E>::exec(a,b);
    E::exec(INDEX,a,b);
    }
    template <typename A>
    static metainline void exec(A& a)
    {
    repeat<INDEX,E>::exec(a);
    E::exec(INDEX,a);
    }
    };
    template <typename E>
    struct repeat<3,E>
    {
    template <typename A, typename B, typename C>
    static metainline void exec(A& a, const B& b, const C& c)
    {
    E::exec(0,a,b,c);
    E::exec(1,a,b,c);
    E::exec(2,a,b,c);
    }
    template <typename A, typename B>
    static metainline void exec(A& a, const B& b)
    {
    E::exec(0,a,b);
    E::exec(1,a,b);
    E::exec(2,a,b);
    }
    template <typename A>
    static metainline void exec(A& a)
    {
    E::exec(0,a);
    E::exec(1,a);
    E::exec(2,a);
    }
    };
    template <typename E>
    struct repeat<2,E>
    {
    template <typename A, typename B, typename C>
    static metainline void exec(A& a, const B& b, const C& c)
    {
    E::exec(0,a,b,c);
    E::exec(1,a,b,c);
    }
    template <typename A, typename B>
    static metainline void exec(A& a, const B& b)
    {
    E::exec(0,a,b);
    E::exec(1,a,b);
    }
    template <typename A>
    static metainline void exec(A& a)
    {
    E::exec(0,a);
    E::exec(1,a);
    }
    };
    template <typename E>
    struct repeat<1,E>
    {
    template <typename A, typename B, typename C>
    static metainline void exec(A& a, const B& b, const C& c)
    {
    E::exec(0,a,b,c);
    }
    template <typename A, typename B>
    static metainline void exec(A& a, const B& b)
    {
    E::exec(0,a,b);
    }
    template <typename A>
    static metainline void exec(A& a)
    {
    E::exec(0,a);
    }
    };
    // ------------------------------------------------------------
    // product
    // ------------------------------------------------------------
    template <typename SCALAR, int SIZE>
    struct product
    {
    enum { INDEX = SIZE - 1 };
    template <typename A, typename B>
    static metainline SCALAR exec(const A& a, const B& b)
    {
    return product<SCALAR,INDEX>::exec(a,b) + a[INDEX] * b[INDEX];
    }
    };
    template <typename SCALAR>
    struct product<SCALAR,4>
    {
    template <typename A, typename B>
    static metainline SCALAR exec(const A& a, const B& b)
    {
    return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3];
    }
    };
    template <typename SCALAR>
    struct product<SCALAR,3>
    {
    template <typename A, typename B>
    static metainline SCALAR exec(const A& a, const B& b)
    {
    return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
    }
    };
    template <typename SCALAR>
    struct product<SCALAR,2>
    {
    template <typename A, typename B>
    static metainline SCALAR exec(const A& a, const B& b)
    {
    return a[0] * b[0] + a[1] * b[1];
    }
    };
    template <typename SCALAR>
    struct product<SCALAR,1>
    {
    template <typename A, typename B>
    static metainline SCALAR exec(const A& a, const B& b)
    {
    return a[0] * b[0];
    }
    };
    // ------------------------------------------------------------
    // evaluators
    // ------------------------------------------------------------
    template <typename SCALAR>
    struct eval_ref
    {
    template <typename A>
    static metainline SCALAR evaluate(int index, const A& a, const int&)
    {
    return a[index];
    }
    };
    template <typename SCALAR>
    struct eval_neg
    {
    template <typename A>
    static metainline SCALAR evaluate(int index, const A& a, const int&)
    {
    return -a[index];
    }
    };
    template <typename SCALAR>
    struct eval_add
    {
    template <typename A, typename B>
    static metainline SCALAR evaluate(int index, const A& a, const B& b)
    {
    return a[index] + b[index];
    }
    };
    template <typename SCALAR>
    struct eval_sub
    {
    template <typename A, typename B>
    static metainline SCALAR evaluate(int index, const A& a, const B& b)
    {
    return a[index] - b[index];
    }
    };
    template <typename SCALAR>
    struct eval_mul
    {
    template <typename A>
    static metainline SCALAR evaluate(int index, const A& a, const SCALAR b)
    {
    return a[index] * b;
    }
    template <typename B>
    static metainline SCALAR evaluate(int index, const SCALAR a, const B& b)
    {
    return a * b[index];
    }
    };
    template <typename SCALAR>
    struct eval_div
    {
    template <typename A>
    static metainline SCALAR evaluate(int index, const A& a, const SCALAR b)
    {
    return a[index] / b;
    }
    };
    template <typename SCALAR, int SIZE>
    struct eval_cross
    {
    template <typename A, typename B>
    static metainline SCALAR evaluate(int, const A&, const B&)
    {
    return 0;
    }
    };
    template <typename SCALAR>
    struct eval_cross<SCALAR,3>
    {
    template <typename A, typename B>
    static metainline SCALAR evaluate(int index, const A& a, const B& b)
    {
    switch ( index )
    {
    case 0: return a[1] * b[2] - a[2] * b[1];
    case 1: return a[2] * b[0] - a[0] * b[2];
    case 2: return a[0] * b[1] - a[1] * b[0];
    default: return 0;
    }
    }
    };
    template <typename SCALAR, int SIZE>
    struct eval_permute
    {
    template <typename A>
    static metainline SCALAR evaluate(int index, const A& a, const int& mask)
    {
    index = (mask >> (index * 2)) & 0x03;
    assert( index < SIZE );
    return a[index];
    }
    };
    // ------------------------------------------------------------
    // expression
    // ------------------------------------------------------------
    template <typename TYPE, typename SCALAR, int SIZE, typename EVALUATOR,
    typename A, typename B>
    class expr
    {
    private:
    const A m_a;
    const B m_b;
    public:
    metainline expr(const A& a, const B& b)
    : m_a(a), m_b(b)
    {
    }
    metainline SCALAR operator [] (int index) const
    {
    assert( index >= 0 && index < SIZE );
    return EVALUATOR::evaluate(index,m_a,m_b);
    }
    metainline expr operator + () const
    {
    return *this;
    }
    metainline expr< TYPE,SCALAR,SIZE,eval_neg<SCALAR>,expr,int >
    operator - () const
    {
    return expr< TYPE,SCALAR,SIZE,eval_neg<SCALAR>,expr,int >(*this,0);
    }
    };
    } // namespace prmath
    #endif


    --
    "What You See isn't What You Get, But What I Give..." -- N. B.
    wogston, Sep 27, 2003
    #4
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. dot_net_junky

    Which is better, C# or C++?

    dot_net_junky, Feb 9, 2005, in forum: ASP .Net
    Replies:
    3
    Views:
    430
    Tor B├ądshaug
    Feb 14, 2005
  2. Replies:
    0
    Views:
    374
  3. Peter Bencsik
    Replies:
    2
    Views:
    804
  4. Andrew Thompson
    Replies:
    8
    Views:
    127
    Premshree Pillai
    Jun 7, 2005
  5. Replies:
    2
    Views:
    39
    Mark H Harris
    May 13, 2014
Loading...

Share This Page