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.

关于内存管理器

内存管理器的接口:
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可以将异常对象的内容输出,然后清空异常内存栈。这种方式可以方式在内存短缺的时候没有内存记录异常信息。

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

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

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

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

On design of parameter configuration.

Parameter configuration

Background
Many crypto components that provide the similar service have completely different configuration schemes.
Although the core service and their interfaces are the similar which can be replaced easily, but there are no unified way to configure them.
For example, AES algorithm support 3 different key lengths, but DES support only one. The recent ECC algorithms actually is not a single algorithm, it is several family of algorithms, each family has a special set of configuration parameters.
We must provide a convenient mechanism to allow user to configure the parameter of each component through a unified way. And this mechanism will allow developers trace the configuration of each component and serialize the configuration data.
This section is about the user scenario of parameter configuration.

User scenario: Setting the configuration of a component to get it ready to provide the service.
1.User has created an object of a component.
2.User know the parameter structure to configure the component from user manual.
3.User create the configuration parameter tree through a parameter tree configuration component.
4.User pass the parameter tree of configuration to the component’s configuration interface which is unified for each component.
5.The component now get in the ready state, ready to provide the major service which it is supposed to.
6.User serialize the state of the components include the configuration. The component will encode the configuration data with given coding rule, but the logical structure will be kept.

Serialization

Background
Because of the polymorphism of the components, the serialization must provide a way to address the proper creator of a component during serializing. This creator is not the constructor of a class, but a static creator which can create an object instance of a component from nothing.

User scenario: Serialization
1.User configure a component, and use it.
2.User use the serializer to save the component, the target shall be a data sink [see stream interface].
–when user saving a polymorphism object, whose component is unknown, the tag of the component will be automatically saved first by the object itself, which is assigned by the container of the component. Then the object will be saved.
–when user saving a known-class object, the tag will not be saved. The object will be saved directly.
3.User use the serializer to load the component, the origin shall be a data source [see stream interface].
–when user loading a polymorphism object, whose component is unknown, the tag of the component will be automatically loaded first. The tag will be used to address the corresponding component creator, which was registered when the component was added to the container.
–when user loading a known-class object, the tag will not be loaded.

To do: list the functions of an component container and a object container.

For each component, the following information shall be registered:

tag, creator

1
2
3
4
template<typename T>
T *creator() {
    return Alloc::alloc<T>();
}

Note, different container may have different tag type.

For codec:
there is no special codec function for any type, but some component is widely used, for example the NatureNumber, OctetString, NullString… such components should be registered into the top level container, and will be widely used in many other components. Those makes such component seems special, like some core-language-type, but actually that is not essential.

For each component, which may be encoded to ASN.1 and BER/CER/DER/PER/XER, shall support both such codec method and our own method. When we serialize a component, the tag of the codec component will be also set and pass to the object, thus the object can choose the corresponding codec function.

1
2
3
4
5
6
7
8
9
10
AsnXer asnXer;
Ner ner;

//the object will be encoded/decoded through ASN+XER codec
asnXer.encode(object, stream);
asnXer.decode(object, stream);

//the object will be encoded/decoded through NER codec
ner.encode(object, stream);
ner.decode(object, stream);

About stream interface usage


-bool Buffering::process(IBuff *pi, OBuff *po) {
- forever {
- // put as much as possible
- po->put(&ctBuff_, ctBuff_.gSize());
- // get as much as possible
- pi->get(&ptBuff_, ptBuff_.pSize());
- if(ptBuff_->isFull() && ctBuff_->isEmpty()) {
- // now, the plaintext buff is fullfilled
- // the ciphertext buff is empty.
- processor_.process(ptBuff, ctBuff);
- // now, the plaintext buff is empty,
- // the ciphertext buff is fullfilled.
- } else {
- break;
- }
- }
- return true;
-}

Aspect management

Each component of a component system may has more than one aspect.

The relationship between aspect and component mostly is orthogonal.

Sometime we may add some new aspect, sometime we may add some new component. But it is not necessary to implement every aspect for every component. If a component don’t provide some aspect, the service that associated with the aspect may not applicable for that component, but the component should be still executable with its implemented aspects.

We can just use a matrix to trace the implemented aspects of each component we have.

By the way, matrix method can also applied to trace the platform depend components for the cross-platform component project. In this scenario, the matrix record the supported platform for each component we have.

Assembling component model: composite component.

Manually assemble the independent component together by hand-coding, is not flexible and lack of management.

Sometime some component can play a manager role when you assemble it with some other components. You can let this component take the responsibility of registering and destroying all the components that it depends on.

But in most cases it’s not a good practice. This makes many components have to burden with the management code. Even worse, sometime the assembling relationship of the whole component system is not tree-formed, some times, it’s a network-like graph.

We need some manager for organize some component sub-system. This can be achieved through creating a common purpose component in assembling component model. We call it a composite component.

A composite component provide following features:

1.registering all the sub-components.
2.recording all the assembling relationships between sub-components.
3.forwarding the service invocation to the corresponding sub-components.
4.destroying all the sub-components before destroyed.

And a composite component may also provides some service such like serialization for saving the assembling relationship of the sub-components.

In practice, composite component sometimes has to manage some external dependency, such some component which may not managed by the current composite component. In this case, the component should register the identity of the external dependency. This identity can be resolved by some higher-level component registry.

(to be continued…)

Assembling component model in software design, my considerations.

Different from other component models, assembling component model explicitly separates the two type of the service port of a component: service provider and service consumer.

Each port references a specification which specify the syntax and semantics of the service, we call it interface specification.

A service provider port, provides service to the customer through a given specification.

A service consumer port, consumes service from the customer through a given specification.

When a service provider of some component can satisfy the service consumer of some component, then when you create objects as instances of the components at runtime, you can simply connect the ports of the objects together, which called assembling. You can assemble many components together to make a complex component system and provide high level services, just like assemble a set of Lego bricks.

How a service provider satisfy a service consumer? If and only if the service provider’s interface specification is a sub-type of the service consumer’s interface specification.

Therefore, it’s not necessary that the provider and the consumer have the exactly same interface specification.

In such an assembling component model, the benefit is you can easily assemble things together, just like to assemble the standardized computer devices. But the draw back is, each time if you want to provide some new independent services, you have to define the syntax and the semantics for the services first, after that you can implement the components which can provide or consume the services. This step will slow down the the response time for the requirement, but reduce the coupling between components, all coupling will just take place between the components and the interface specifications.

In such component model, interfaces are no more auxiliaries of the components, actually they are the central concepts of a system.