先说结论
DI < IOC $\approx$ DIP

DIP 依赖反转原则 IOC 控制反转

ass
图1高层对象A依赖于底层对象B的实现;图2中,把高层对象A对底层对象的需求抽象为一个接口A,底层对象B实现了接口A,这就是依赖反转

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
class shape {
public:
virtual double get_area() const = 0;
};

class Rectangle: public shape {
public:
double h;
double w;
double get_area() const {
return h * w;
}
};

class Disk: public shape{
public:
double radius;
double get_area() const {
return 3.1415926 * radius * radius;
}
};

class Viewer{
public:
void show_area(const shape & sp){
std::cout << sp.get_area() << std::endl;
}
};

int main(){
Viewer view;
Rectangle rect;
rect.h = 1;
rect.w = 2;
Disk disk;
disk.radius = 3;
view.show_area(disk);
view.show_area(rect);
}

Java 里interface 可以用interface key word 或者abstract class key word来实现, 两者的区别在于

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
interface shape{
public double get_area();
}

class Rectangle implements shape{
double h;
double w;
public double get_area(){
return h * w;
}
}

class Disk implements shape{
double r;
public double get_area(){
return 3.1415926 * r * r;
}
}

class Viewer {
public void show_area(shape sp){
System.out.println(sp.get_area());
}
}

public class testPackage {
public static void main(String[] args) {
Viewer view = new Viewer();
Disk disk = new Disk();
Rectangle rect = new Rectangle();
rect.h = 1;
rect.w = 2;
disk.r = 3;
view.show_area(rect);
view.show_area(disk);
}
}

控制反转与依赖反转本身是十分相似的,但是IOC更进一步要解决Viewer class 直接私有一个shape 对象的例子.

Answering only the first part. What is it?

Inversion of Control (IoC) means to create instances of dependencies first and latter instance of a class (optionally injecting them through constructor), instead of creating an instance of the class first and then the class instance creating instances of dependencies. Thus, inversion of control inverts the flow of control of the program. Instead of the callee controlling the flow of control (while creating dependencies), the caller controls the flow of control of the program.

REST means representation state transfer

It is architectural style for distributed hypermedia systems and was first presented by Roy Fielding in 2000 in his famous dissertation.
REST defines 6 architectural constraints which make any web service – a true RESTful API.

  1. Uniform interface
  2. Client–server
  3. Stateless
  4. Cacheable
  5. Layered system
  6. Code on demand (optional)
    Read more »

Linux 面试

C++ 面试

new features

C++11

1
2
3
4
5
6
7
8
9
10
Core language features

auto and decltype
rvalue references
move constructors and move assignment operators
attributes
lambda expressions
range-for (based on a Boost library)
static_assert (based on a Boost library)
smart pointers

语法

new vs malloc

1
2
3
4
5
6
7
8
9
10
11
12
class A{...}
A * ptr = new A;
A * ptr = (A *)malloc(sizeof(A)); //需要显式指定所需内存大小sizeof(A); 需要将void * cast 为 A* type
// new/delete would call the constructor of class A

// For array
A * ptr = new A[10];//分配10个A对象
// 使用new[]分配的内存必须使用delete[]进行释放
delete [] ptr;

int * ptr = (int *) malloc( sizeof(int)* 10 );//分配一个10个int元素的数组
free(ptr)

STL 非官方实现

实现一个unique_ptr

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
template<typename T>
class MyUniquePtr
{
public:
explicit MyUniquePtr(T* ptr = nullptr)
:mPtr(ptr)
{}

~MyUniquePtr()
{
if(mPtr)
delete mPtr;
}

MyUniquePtr(MyUniquePtr &&p) noexcept;
MyUniquePtr& operator=(MyUniquePtr &&p) noexcept;

MyUniquePtr(const MyUniquePtr &p) = delete;
MyUniquePtr& operator=(const MyUniquePtr &p) = delete;

T* operator*() const noexcept {return mPtr;}
T& operator->()const noexcept {return *mPtr;}
explicit operator bool() const noexcept{return mPtr;}

void reset(T* q = nullptr) noexcept
{
if(q != mPtr){
if(mPtr)
delete mPtr;
mPtr = q;
}
}

T* release() noexcept
{
T* res = mPtr;
mPtr = nullptr;
return res;
}
T* get() const noexcept {return mPtr;}
void swap(MyUniquePtr &p) noexcept
{
using std::swap;
swap(mPtr, p.mPtr);
}
private:
T* mPtr;
};

template<typename T>
MyUniquePtr<T>& MyUniquePtr<T>::operator=(MyUniquePtr &&p) noexcept
{
swap(*this, p);
return *this;
}

template<typename T>
MyUniquePtr<T> :: MyUniquePtr(MyUniquePtr &&p) noexcept : mPtr(p.mPtr)
{
p.mPtr == NULL;
}

实现一个shared_ptr

这件事情主要在于实现一个带有引用计数的指针, 并支持父类子类的转换

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
template <typename T>
class mySmartPtr
{
public:
// 构造函数 默认为空
mySmartPtr(): pointer(0), counter(0)
{
}

// 形参为指针的构造函数
mySmartPtr(T* p): pointer(0), counter(0){
*this = p;
}

// 复制构造函数
mySmartPtr(const mySmartPtr<T> &p):
pointer(p.pointer), counter(p.counter){
Increase();
}

~mySmartPtr(){
Decrease();
}

// 指针的赋值操作符,类型不同,不是自赋值
mySmartPtr operator=(T* p){
Decrease();
if (p){
pointer = p;
counter = new int(1);
}
else {
pointer = 0;
counter = 0;
}
return *this;
}

// 智能指针赋值操作符
mySmartPtr operator=(mySmartPtr<T> &p){
// 处理自赋值
if (this != &p){
Decrease();
pointer = p.pointer;
counter = p.counter;
Increase();
}
return *this;
}

operator bool() const{
return counter != 0;
}

// ×操作符重载
T* operator*() const{
return this;
}

// ->操作符重载
T* operator->() const{
return pointer;
}

// 返回底层指针
int getPtrPointer() const{
return *pointer;
}

// 返回引用计数
int getPtrCounter() const{
return *counter;
}

// 处理父类子类的情况, ptr<derived>不能访问 ptr<based>的内部对象
template<typename C> friend class mySmartPtr;

template<typename C>
mySmartPtr(const mySmartPtr<C> &p):
pointer(p.pointer), counter(p.counter){
Increase();
}

template<typename C>
mySmartPtr<T> & operator=(const mySmartPtr<C> &p){
Decrease();
pointer = p.pointer;
counter = p.counter;
Increase();
return *this;
}

// 处理内部使用 dynamic_cast做判断的转换,失败则空指针
template<typename C>
mySmartPtr<C> Cast() const{
C* converted = dynamic_cast<C*>(pointer);
mySmartPtr<C> result;
if (converted){
result.pointer = converted;
result.counter = counter;
result.Increase();
}
return result;
}

private:
T* pointer;
int* counter;

void Increase(){
if (counter)
++*counter;
}

void Decrease(){
if (counter && --*counter == 0){
delete pointer;
delete counter;
counter = 0;
pointer = 0;
}
}

};

C style 多态

内存池和alloc 实现

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
#ifndef _MEMORY_POOL_H_
#define _MEMORY_POOL_H_

#include <stdint.h>
#include <mutex>

template<size_t BlockSize, size_t BlockNum = 10>
class MemoryPool
{
public:
MemoryPool()
{
std::lock_guard<std::mutex> lk(mtx); // avoid race condition

// init empty memory pointer
free_block_head = NULL;
mem_chunk_head = NULL;
}

~MemoryPool()
{
std::lock_guard<std::mutex> lk(mtx); // avoid race condition

// destruct automatically
MemChunk* p;
while (mem_chunk_head)
{
p = mem_chunk_head->next;
delete mem_chunk_head;
mem_chunk_head = p;
}
}

void* allocate()
{
std::lock_guard<std::mutex> lk(mtx); // avoid race condition

// allocate one object memory

// if no free block in current chunk, should create new chunk
if (!free_block_head)
{
// malloc mem chunk
MemChunk* new_chunk = new MemChunk;
new_chunk->next = NULL;

// set this chunk's first block as free block head
free_block_head = &(new_chunk->blocks[0]);

// link the new chunk's all blocks
for (int i = 1; i < BlockNum; i++)
new_chunk->blocks[i - 1].next = &(new_chunk->blocks[i]);
new_chunk->blocks[BlockNum - 1].next = NULL; // final block next is NULL

if (!mem_chunk_head)
mem_chunk_head = new_chunk;
else
{
// add new chunk to chunk list
mem_chunk_head->next = new_chunk;
mem_chunk_head = new_chunk;
}
}

// allocate the current free block to the object
void* object_block = free_block_head;
free_block_head = free_block_head->next;

return object_block;
}

void* allocate(size_t size)
{
std::lock_guard<std::mutex> lk(array_mtx); // avoid race condition for continuous memory

// calculate objects num
int n = size / BlockSize;

// allocate n objects in continuous memory

// FIXME: make sure n > 0
void* p = allocate();

for (int i = 1; i < n; i++)
allocate();

return p;
}

void deallocate(void* p)
{
std::lock_guard<std::mutex> lk(mtx); // avoid race condition

// free object memory
FreeBlock* block = static_cast<FreeBlock*>(p);
block->next = free_block_head; // insert the free block to head
free_block_head = block;
}

private:
// free node block, every block size exactly can contain one object
struct FreeBlock
{
unsigned char data[BlockSize];
FreeBlock* next;
};

FreeBlock* free_block_head;

// memory chunk, every chunk contains blocks number with fixed BlockNum
struct MemChunk
{
FreeBlock blocks[BlockNum];
MemChunk* next;
};

MemChunk* mem_chunk_head;

// thread safe related
std::mutex mtx;
std::mutex array_mtx;
};

#endif // !_MEMORY_POOL_H_

Assume you have a binary or jar in some directory and you want to run it as daemon (which will not be killed as long as the os is runing)

Read more »

Nginx is a powerful web proxy server. Recently, I used it to deploy my GoReactChatApp on VPS. The remaining applies to a ubuntu 18 server

Install

1
2
sudo apt update
sudo apt install nginx

Read more »

Recently, I have had a chance to build and test one of my largest project on HPC cluster. It took me sometime to correctly build and run it.

build

1
2
3
4
5
6
7
8
9
10
11
12
13
module purge
module load gcc/9.1.0
module load hdf5/intel/1.10.0p1
module load cmake/intel/3.11.4
module load mpfr/gnu/3.1.5
module load gmp/gnu/6.1.2
module load boost/intel/1.71.0
export CXX=$(which g++)
export CC=$(which gcc)
export GMP_DIR=${GMP_ROOT}
export MPFR_DIR=${MPFR_ROOT}
export BOOST_DIR=${BOOST_ROOT}
cmake -DGMP_LIBRARIES="/share/apps/gmp/6.1.2/gnu/lib/libgmp.so.10.3.2" -DGMP_INCLUDE_DIR="/share/apps/gmp/6.1.2/gnu/include" -DMPFR_LIBRARIES="/share/apps/mpfr/3.1.5/gnu/lib/libmpfr.so.4.1.5" -DMPFR_INCLUDES="/share/apps/mpfr/3.1.5/gnu/include" -DPython_LIBRARIES="/share/apps/python3/3.6.3/intel/lib/libpython3.6m.so" -DPYTHON_EXECUTABLE=“/share/apps/python3/3.6.3/intel/bin/python” ..
Read more »

Definitions

Tutte embedding is method to generate drawing of 3-(vertex)-connected graphs. With the following definitions.

Definition A graph $G$ is called $k$-vertex-connected if removing any $k$ vertices from $G$ would let $G$ remain connected.

Definition A drawing $z$ of graph $G$ is a embedding of $G\rightarrow \mathbb{R}^2$. $G$ is called planar if such a drawing exists.

Definition A Tutte embedding of planar graph $G$ is a drawing $z$ of $G$ such that

  1. There is a face $F$ of $G$ such that $z$ maps the vertices of $F$ to the corners of a strictly convex polygon so that every edge of the face joins consecutive corners of the polygon
  2. Every vertex not in $F$ lies at the barycenter of its neighbors.
  3. Each edge is represented as a straight line.
    By definition Tutte embedding is free from self-intersections because it is a drawing.
    Read more »
0%