Pipe & Filter

For a filter:
before you call ini(), the input will not be consumed.
after you call fin(), the output will not be produced.
input before ini() and output after fin() may cause ended()
filter doesn’t have any flush method. because filter doesn’t own any buffer, filter always process as much as possible.
      +---------------+  +-------+-------+  +---------------+
      | FilterSrcBase |  |  FilterBase   |  | FilterSnkBase |
      +---------------+  +---------------+  +---------------+
      |     ini()     |  |   process()   |  |     fin()     |
      |    iend()     |  |               |  |    oend()     |
      +---------------+  +---------------+  +---------------+
              +                  +                  +        
             /_\                /_\                /_\      
              |                  |                  |        
              +                  +                  +        
              |\                /|\                /|        
              | +-----+  +-----+ | +-----+  +-----+ |        
              |        \/        |        \/        |        
              |        /\        |        /\        |        
              | +-----+  +-----+ + +-----+  +-----+ +        
              |/                \|/                \|        
              +                  +                  +        
              |                  |                  |        
      +-------+-------+  +-------+-------+  +---------------+
      |   FilterSrc   |  |    Filter     |  |   FilterSnk   |
      +---------------+  +---------------+  +---------------+
      |process -> get |  |               |  |process -> put |
      |     get()     |  |               |  |     put()     |
      +---------------+  +---------------+  +---------------+

For a pipe:
when you call pumping, it will try to process data as much as possible, till the end of input stream or output stream.
in blocked mode, pump will be blocked if there temporarily no data can be processed.
in non-blocked mode, pump will return if there temporarily no data can be processed.
if there is a open end of a pipeline system, this open end can behave like a filter thing, you can get a FilterSrc or a FilterSnk or a Filter object from the open end.
      +---------------+  +-------+-------+  +---------------+
      |  PipeSrcBase  |  |    PipeBase   |  |  PipeSnkBase  |
      +---------------+  +---------------+  +---------------+
      |  attachSnk()  |  |     pump()    |  |  attachSrc()  |
      |   iflush()    |  |  attachBuf()  |  |    oflush()   |
      +---------------+  +---------------+  +---------------+
              +                  +                  +        
             /_\                /_\                /_\      
              |                  |                  |        
              +                  +                  +        
              |\                /|\                /|        
              | +-----+  +-----+ | +-----+  +-----+ |        
              |        \/        |        \/        |        
              |        /\        |        /\        |        
              | +-----+  +-----+ + +-----+  +-----+ +        
              |/                \|/                \|        
              +                  +                  +        
              |                  |                  |        
      +-------+-------+  +-------+-------+  +---------------+
      |    PipeSrc    |  |      Pipe     |  |    PipeSnk    |
      +---------------+  +---------------+  +---------------+
      |  pump -> push |  |               |  |  pump -> pull |
      |     push()    |  |               |  |     pull()    |
      +---------------+  +---------------+  +---------------+

An idiomatic implementation of operator=()

Besides destructors and deallocation functions, common error-safety techniques rely also on swap operations never failingin this case, not because they are used to implement a guaranteed rollback, but because they are used to implement a guaranteed commit. For example, here is an idiomatic implementation of operator= for a type T that performs copy construction followed by a call to a no-fail Swap:

1
2
3
4
T& T::operator=( const T &other ) {
    T temp( other );
    Swap( temp );
}

——————————-
A swap function typically looks like this, where U is some user-defined type:

1
2
3
4
5
6
7
8
9
10
class T {// …
public:
    void swap( T& rhs ) {
        member1_.swap( rhs.member1_ );
        std::swap( member2_, rhs.member2_ );
    }
private:
    U member1_;
    int member2_;
};

For primitive types and for standard containers, std::swap will do. Other classes might implement swapping as a member function under various names.

Consider using swap to implement copy assignment in terms of copy construction. The following implementation of operator= provides the strong guarantee (see Item 71), although at the price of creating an extra object, which can be inappropriate if there are more efficient ways to perform error-safe assignment for T objects:

1
2
3
4
5
6
7
8
9
T& T::operator=( const T& other ) { // good: Variant #1 (traditional)
    T temp( other );
    swap( temp );
    return *this;
}
T& T::operator=( T temp ) { // good: Variant #2 (see Item 27)
    swap( temp ); // note: temp passed by value
    return *this;
}

Never use the trick of implementing copy assignment in terms of copy construction by using an explicit destructor followed by placement new, even though this trick still crops up regularly in C++ forums. (See also Item 99.) That is, never write:

1
2
3
4
5
6
7
T& T::operator=( const T& rhs ) { // bad: an anti-idiom
    if( this != &rhs ) {
        this->~T(); // this technique is evil
        new (this) T( rhs ); // (see [Sutter00] §41)
    }
    return *this;
}

!!!! *this has been destroyed, there is no guarantee that the following program will work. Some destructor may delete this !!!!

Recyclable:

Only recyclable should have a pure virtual destructor.
When you try to destroy a Lego object, you should first dynamically cast the object to be a Recyclable object.

All the other classes destructor shall be protected and non-virtual.

Beware of inadvertently hiding overloads in the base class

Beware of inadvertently hiding overloads in the base class. For example:


class Base{// …
virtual void Foo( int );
virtual void Foo( int, int );
void Foo( int, int, int );
};

class Derived : public Base {// …
virtual void Foo( int ); // overrides Base::Foo(int), but hides the others
};

Derived d;
d.Foo( 1 ); // ok
d.Foo( 1, 2 ); // error (oops?)
d.Foo( 1, 2, 3 ); // error (oops?)

If the base class’s overloads should be visible, write a using declaration to redeclare them in the derived class:


class Derived : public Base {// …
virtual void Foo( int ); // overrides Base::Foo(int)
using Base::Foo; // bring the other Base::Foo overloads into scope
};

RC4 illustrated

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
S-box Transform during Key Stream Generation (2 cycles):
|i -->[++]-----+-------------+---- -->[++]-----+-------------+----
|              |             |                 |             |    
|              |             |                 |             |    
|              |             |                 |             |    
|              |             v                 |             v    
|              v            \=/                v            \=/  
|S ===========[S]==========>|X|=== ===========[S]==========>|X|===
|              |            /=\                |            /=\  
|              |             ^                 |             ^    
|              |             |                 |             |    
|              |             |                 |             |    
|              v             |                 v             |    
|j ---------->[+]------------+---- ---------->[+]------------+----

|     ====    ===           ===
|      ^^      ^^            ^^
|      ||      ||            ||
|      ||      ||            ||
|      i++   j+=S[i]   Swap(S[i],S[j])

Key Stream Generation (2 cycles):
|i -->[++]--+----------------+---- -->[++]--+----------------+----
|           |                |              |                |    
|           |                |              |                |    
|           |  +->[+]--+     |              |  +->[+]--+     |    
|           |  |   ^   |     v              |  |   ^   |     v    
|           v  |   |   v    \=/             v  |   |   v    \=/  
|S ========[S]====[S]=[S]==>|X|=== ========[S]====[S]=[S]==>|X|===
|           |  |   ^   |    /=\             |  |   ^   |    /=\  
|           |  |   |   v     ^              |  |   |   v     ^    
|           +--+   |  out    |              +--+   |  out    |    
|           |      |         |              |      |         |    
|           v      |         |              v      |         |    
|j ------->[+]-----+---------+---- ------->[+]-----+---------+----

|     ==== ===    =======   ===
|      ^^   ^^       ^^      ^^
|      ||   ||       ||      ||
|      ||   ||       ||      ||
|      || j+=S[i]    ||Swap(S[i],S[j])
|      i++    out=S[S[i]+S[j]]

Key Scheduling (2 cycles):
|i ----+----+--------+---->[++]--- ----+----+--------+---->[++]---
|      |    |        |                 |    |        |            
|      |    |        |                 |    |        |            
|      |    |        |                 |    |        |            
|      |    |        v                 |    |        v            
|      v    |       \=/                v    |       \=/          
|S ===[S]==========>|X|=========== ===[S]==========>|X|===========
|      |    |       /=\                |    |       /=\          
|      |    v        ^                 |    v        ^            
|      |   [K]       |                 |   [K]       |            
|      |    |        |                 |    |        |            
|      v    v        |                 v    v        |            
|j -->[+]->[+]-------+------------ -->[+]->[+]-------+------------

|     ========      ===    ====
|        ^^          ^^     ^^
|        ||          ||     ||
|        ||          ||     ||
|   j+=K[i]+S[i]     ||     i++
|              Swap(S[i],S[j])

Exceptions in C++ std


exception
----bad_exception
----logic_error
--------domain_error
--------invalid_argument
--------length_error
--------out_of_range
----runtime_error
--------range_error
--------overflow_error
--------underflow_error
----bad_alloc
----bad_cast
----bad_typeid
----ios_base::failure

Conventions:

  1. memory manageable:
  2. A component class, for general purpose, shall be able to be configured with a memory manager. This is a recursive convention: every member of this class shall also be able to be configured with the same memory manager as this class.
    Some special purposed class, may be configurable with complex memory management strategy, different member may use different strategy.

  3. cloneable, copy constructible:
  4. A component class shall be copy-constructible, which means it shall have a copy constructor. Together memory manageable, it will be cloneable.

  5. default create-able, default constructible:
  6. A component class shall be default-constructible, which means it shall have a default constructor. Together memory manageable, it will be default create-able.

  7. recyclable, destructible:
  8. A component class shall be destructible, which means all the resource shall be released when being destructed. Together memory manageable, it will be recyclable.

  9. general configurable:
  10. A component object shall have a setup method through which user can completely setup the state of the object with another component object, which means user setup an object with given parameter, the result shall be the exactly the same as create an object with give parameter.

  11. exceptional safe:
  12. A component class shall not leak any resource even when an exception is thrown. Any resource must be protected before the codes which may potentially throw exceptions are executed.

  13. naming:
  14. Every template class or function include member function shall end with a ‘T’.

  15. parameter, pointer or reference?:
  16. non-const object: T *
    non-const array : T *
    const object: T const &
    const array : T const *

  17. Avoid calling virtual functions in constructors and destructors
  18. In constructors and destructors, the virtual function either not ready to work or expired, because the derived class either hasn’t been constructed or has already been destructed.

Because macro is dangerous, maybe we should name the header file which contain macros .mac, and those file shall only be used internally, and shall never be include before other type of header files.

C++ exception


|exception
| bad_exception
| logic_error
| domain_error
| invalid_argument
| length_error
| out_of_range
| runtime_error
| overflow_error
| range_error
| underflow_error
|
|terminate_handler
|unexpected_handler
|set_terminate
|set_unexpected
|terminate
|unexpected
|uncaught_exception

MSVC special command line options

/Zl will avoid the compiler to generate the library name information in the object file, thus you can generate runtime library independent object files.

/E command line compiler option to generate preprocessed code.

In your project property page: Configuration Properties | C/C++ | Command Line, you can add /E option manually in Additional Options box.

Then when you compile the code, the preprocessed code will displayed in the Output window.

关于内存管理器

内存管理器的接口:
interface MemManager {
virtual ~MemManager() throw() = 0 {}
virtual void acquire(void **, size_t) throw(bad_alloc) = 0;
virtual void release(void **, size_t) throw(bad_alloc) = 0;
};
貌似release函数的size_t参数是多于的,但实际上对于无结构堆栈和无结构环形内存缓冲区这种内存管理器来说,这个参数可能是必要的,也就是说内存管理器为了提高效率,并不希望记录内存尺寸,由使用者自行记录。对于那些记录内存尺寸的内存管理器,可以简单地忽略这个参数。

如果一个对象希望能够被任何类型的内存管理器所管理而不仅仅是自由内存堆,就应该记录对象的尺寸。

使用一个内存管理器来管理内存,就必须保证内存管理器对象不会在被使用内存管理器的对象都释放掉之前退出。如果一个内存管理器推出的时候仍然有内存未被释放,那么就说明程序有错误。由于析构函数最好不要抛出异常,一种处理方式是使用assert,另一种方式是使用日志记录。但是在发布版本中,assert不存在,日志记录也改变不了内存管理器对象已经退出这个事实,如果有其他的对象再次使用这个内存管理器释放内存,就会造成灾难。一种技术是通过引用计数来管理内存管理器的生命周期。除非有十足的把握确保内存管理器一定会在所有的用户都退出之后才会退出,否则不要使用局部变量式的内存管理器。全局的(生命期跨越作用域的)内存管理器自身的内存最好直接由全生命期的内存管理器例如malloc和free来管理。

栈式的FILO内存管理器由于非常简单高效,可以用于实现异常抛出过程中的内存管理。可以指定一个全局的静态内存区作为异常处理内存区。每当抛出异常,就在这个内存区中分配内存并且记录内容,中间过程有转发式的catch-process-throw,也再其中分配内存并记录当前状态信息。最终终止异常处理的catch可以将异常对象的内容输出,然后清空异常内存栈。这种方式可以方式在内存短缺的时候没有内存记录异常信息。

Buffer类还是需要的

如果每个source、sink、filter使用自己的buffer,那么buffer就会冗余,并且有多余的内存拷贝。
应该是每个source、sink、filter之间都通过buffer相连接,这样就不会重复了。

适应引用计数机制的对象结构(续)

以上过程有可能会相当耗费时间,尤其是对象引用关系非常复杂的时候。如何才能减少时间消耗呢?另外一种方法可能是将共享指针和弱指针合二为一,由一个状态来标记该智能指针是共享指针还是弱指针。一次演习的时候,如果发现了回路,那么终点的共享指针就自动转化为弱指针。弱指针的意思是:这里发现了一个回路!!!为了防止对象删除之后某些弱指针失效,还需要采用boost库删除计数器的技术,只有在弱计数也变为0的时候,计数器才删除。不过删除对象的时候就不能仅仅由于共享计数为0。如果对象的弱计数不为0而共享计数为0,那么说明该对象被循环引用,而在回路上的某些对象仍被回路外的一些对象所引用。此时该对象简单地把自身所拥有的所有共享指针全部变为弱指针即可,不必被删除。如果一个对象拥有的智能指针全都是弱指针,那么一旦这个对象的引用计数变为0,就应该被删除。一个引用基数为0,并且拥有的指针全部是弱指针的对象,当它再一次被某个共享指针所引用的时候,这个共享指针肯定是从某个指向该对象的弱指针上获取的引用,此时该弱指针应该被提升位共享指针。因为此时从该对象出发的所有智能指针全都是弱指针,提升一个指向该对象的共享指针是不会造成共享指针回路的。

这些想法不完善,有很多漏洞,需要进一步思考。

fire_drill()技术是在删除的时候做演习来检测回路。与之相应还可以在增加一个对象的引用计数的时候做演习,在早期发现回路。对于回到自身的引用,不计入引用计数???

Compile Time Assert & Check

//—-CompileTimeAssert—-
template<bool> struct CompileTimeAssert;
template<> struct CompileTimeAssert<true> {};

//—-CompileTimeChecker—-
template<bool> struct CompileTimeChecker {
CompileTimeChecker(…);
};
template<> struct CompileTimeChecker<false> { };
#define STATIC_CHECK(expr, msg) {\
class ERROR_##msg {};\
(void)sizeof(CompileTimeChecker<(expr) != 0>((ERROR_##msg())));\
}

// usage:
template <class To, class From>
To safe_reinterpret_cast(From from) {
STATIC_CHECK(sizeof(From) <= sizeof(To), Destination_Type_Too_Narrow);
return reinterpret_cast<To>(from);
}

void* somePointer;
char c = safe_reinterpret_cast<char>(somePointer); // error
//error: ‘CompileTimeChecker<false> (safe_reinterpret_cast::ERROR_Destination_Type_Too_Narrow (__cdecl *)(void))’: illegal sizeof operand

Finalize a C++ class

// Prevent inheritance from a class

#define DECLARE_FINALIZE(C_)\
class C_;\
class Finalize_##C_{\
friend class C_;\
private:\
Finalize_##C_() {}\
}

#define FINALIZE(C_) virtual Finalize_##C_

// Test finalizing
DECLARE_FINALIZE(Base);
class Base: FINALIZE(Base) { // This finalizes ‘Base’
};
class Derived : public Base{
};

Base b_;
Derived d_; // error: cannot access private constructor of ‘Finalize_Base’

Locker

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
//mutex.cpp
const pthread_mutex_t RECURISIVE_MUTEX = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;

class PthreadRecursiveMutex {
public:
    PthreadRecursiveMutex(): mutex_(RECURISIVE_MUTEX) {
    }
    ~PthreadRecursiveMutex() {
    }
    bool enter() {
        return (0==pthread_mutex_lock(&mutex_));
    }
    bool leave() {
        return (0==pthread_mutex_unlock(&mutex_));
    }
private:
    pthread_mutex_t mutex_;
};

//lock.cpp
template<class Lockable>
class Lock {
    Lockable &rlockable_;
public:
    Lock(Lockable &cs) rlockable_(cs) {
        rlockable_.enter();
    }
    ~Lock() {
        rlockable_.leave();
    }
};

//ssx31b driverhardware

class SomeHardware {
    PthreadRecursiveMutex mutex_;
public:
    bool enter() {
        return mutex_.enter();
    }
    bool leave() {
        return mutex_.leave();
    }
...
};

SomeHardware g_theHardware;

//hardware access function implementation:
...
member_fun(...) {
...
{
Lock<SomeHardware> _(g_theHardware);
...
}
...
}

Wrapper

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
/*
Wrapping C++ Member Function Calls
----Bjarne Stroustrup
Published in the June 2000 issue of "The C++ Report".
*/


template<class T, class Pref, class Suf> class Wrap;
template<class T, class Suf>
class Call_proxy {
    T*p;
    mutable bool own;
    Suf suffix;
    Call_proxy(T *pp, Suf su): p(pp), own(true), suffix(su) {
    }//restrict creation
    Call_proxy &operator=(const Call_proxy &);//prevent assignment
public:
    template<class U, class P, class S> friend class Wrap;
    Call_proxy(const Call_proxy &a): p(a.p), own(true), suffix(a.suffix) { a.own = false; }
    ~Call_proxy(){ if(own)suffix(); }
    T *operator->() const { return p; }
    /* !!! this can work because: aWrap->f() <===> aWrap.operator->()->f() <===> aWrap.operator->().operator->()->f() */
};

template<class T, class Pref, class Suf>
class Wrap {
    T *p;
    int *owned;
    void incr_owned() const { if(owned) ++*owned; }
    void decr_owned() const { if(owned && --*owned==0){ delete p; delete owned; } }
    Pref prefix;
    Suf suffix;
public:
    Wrap(T &x,Pref pr, Suf su): p(&x), owned(0), prefix(pr), suffix(su) {}
    Wrap(T *pp,Pref pr,Suf su): p(pp), owned(new int(1)), prefix(pr), suffix(su){}
    Wrap(const Wrap &a): p(a.p), owned(a.owned), prefix(a.prefix), suffix(a.suffix) { incr_owned(); }
    Wrap &operator=(const Wrap &a) {
        a.incr_owned();
        decr_owned();
        p=a.p;
        owned=a.owned;
        prefix=a.prefix;;
        suffix=a.suffix;
        return*this;
    }
    ~Wrap(){decr_owned();}
    Call_proxy<T, Suf> operator->() const { prefix(); return Call_proxy<T, Suf>(p,suffix); }
    T *direct() const { return p; }// extract pointer to wrapped object
    Wrap &self() { return *this; }// to enable recursive wrapper.
};

#include<iostream>
using namespace std;
void prefix() { cout << "prefix "; }
void suffix() { cout << " suffix\n"; }
class X{ // one user class
public:
    X() { cout << "make an X\n"; }
    ~X(){ cout << "destroy an X\n"; }
    int f() const { cout << "f()"; return 1; }
    void g() const { cout << "g()"; }
};
class Y{ // another user class
public:
    Y() { cout << "make a Y\n"; }
    ~Y() { cout << "destroy a Y\n"; }
    void h() const { cout << "h()"; }
};
struct Pref { void operator()() const { cout << "Pref "; } };
struct Suf { void operator()() const { cout << " Suf\n"; } };

#if 0

/* 7 Parameterization, lock example */
struct Lock {
    sys_lock lck;
    Lock(sys_lock &x): lck(x) {}
    void operator()() const { grab(lck); }
};
struct Unlock{
    sys_lock lck;
    Unlock(sys_lock &x): lck(x) {}
    void operator()() const { release(lck); }
};
Wrap<X, Lock, Unlock> x3(x, Lock(screen_lock), Unlock(screen_lock));
Wrap<X, Lock, Unlock> x4(y, Lock(lock3a), Unlock(lock3a));

/* 8 Notational Convenience */
template<class T>
class Shared: public Wrap<T, Lock, Unlock>
{
public:
    Shared(T &obj, sys_lock &lck): Wrap<T, Lock, Unlock>(obj, Lock(lck), Unlock(lck)) {}
    Shared(T *ptr, sys_lock &lck): Wrap<T, Lock, Unlock>(ptr, Lock(lck), Unlock(lck)) {}
};
Shared<X> x3(x, screen_lock);
Shared<X> x4(y, lock3);

template<class T>
struct Tracer: public Wrap<T, void(*)(), void(*)()> {
    Tracer(T &x): Wrap<T, void(*)(), void(*)()>(x, trace_on, trace_off) {}
};
X x;
Tracer<X> xx(x);
typedef Tracer<X> Xtracer;
Xtracer xxx(x);

#endif

int main() {
    Wrap<X, void(*)(), void(*)()> xx(new X, prefix, suffix);
    Wrap<Y, void(*)(), void(*)()> yy(new Y, prefix, suffix);
    Wrap<X, void(*)(), void(*)()> x2 = xx;

    X x;
    Wrap<X, Pref, Suf> x3(x, Pref(), Suf());

    if(xx->f()) cout<<"done\n";

    xx->g();
    x2->g();
    xx=x2;
    x2->g();
    x3->g();
    yy->h();

    // recurisive wrapper:
    cout << "!!!\n";
    Wrap<Wrap<X, void(*)(), void(*)()>, Pref, Suf> ww(new Wrap<X, void(*)(), void(*)()>(new X, prefix, suffix), Pref(), Suf());
    ww->operator->()->g(); // tasteless, but it works.
    ww->self()->f(); // using self() function.
    cout << "!!!\n";

    return 0;
}