libsvmコード読解:svm.cpp浅談と関数ポインタ


Update:2014-2-26 LinJM@HQU『libsvmコラムアドレス:http://blog.csdn.net/column/details/libsvm.html 』
svm.cpp浅談
svm.cppは全部で3159行のコードがあって、svmアルゴリズムの核心機能を実現して、中は全部でCache、Kernel、ONE_がありますCLASS_Q、QMatrix、Solver、Solver_NU、SVC_Q、SVR_Q 8クラス(下図1に示す)であり、それらの継承と組合せ関係は図2、図3に示す.これらのクラスではCache、Kernel、Solverがコアクラスであり、アルゴリズム全体を支える役割を果たす.今後のブログではこれら3つのコアクラスについて重点的な注釈分析を行い、またsvm.cppのsvm_train関数についても注釈分析を行う.
libsvm代码阅读:svm.cpp浅谈和函数指针_第1张图片
図1
libsvm代码阅读:svm.cpp浅谈和函数指针_第2张图片
図2
libsvm代码阅读:svm.cpp浅谈和函数指针_第3张图片
図3
次にsvmを見てみましょう.cppの最初の数行のコードは、主にいくつかのinline関数であり、inline関数は頻繁に小関数を呼び出してスタック空間を大量に消費する問題を解決する.
#include "svm.h"
int libsvm_version = LIBSVM_VERSION;

typedef float Qfloat;
typedef signed char schar;

//inline                  ;static       /     ,               
#ifndef min
template <class T> static inline T min(T x,T y) { return (x<y)?x:y; }
#endif
#ifndef max
template <class T> static inline T max(T x,T y) { return (x>y)?x:y; }
#endif
template <class T> static inline void swap(T& x, T& y) { T t=x; x=y; y=t; }

次はclone関数です(
完全なクローン関数で、操作が終わった後、内部のすべてのデータはポインタと完全に同じです)、最初に見たときcloneのパラメータT*&dstについてよく知らなかったが、後でこれがポインタの参照であることを知った.つまり、関数の中でポインタの変化はパラメータの変化に等しいということです.memcpyと*&の具体的な説明については、私のブログでよく見られるC++の知識を参照してください.
template <class S, class T> static inline void clone(T*& dst, S* src, int n)
{
	dst = new T[n];
	memcpy((void *)dst,(void *)src,sizeof(T)*n);
}

次はべき乗関数で、よく書けています.
べき乗数が偶数の場合に最適化されます.powi(3,5)の場合、このような3*(81)、すなわち3^5=3*(3^4)
static inline double powi(double base, int times)
{
	double tmp = base, ret = 1.0;

	for(int t=times; t>0; t/=2)
	{
		if(t%2==1) ret*=tmp;
		tmp = tmp * tmp;
	}
	return ret;
}

以下はデバッグ情報出力です
static void print_string_stdout(const char *s)
{
	fputs(s,stdout);
	fflush(stdout);
}
static void (*svm_print_string) (const char *) = &print_string_stdout;

#if 1
static void info(const char *fmt,...)
{
	char buf[BUFSIZ];
	va_list ap;
	va_start(ap,fmt);
	vsprintf(buf,fmt,ap);
	va_end(ap);
	(*svm_print_string)(buf);
}
#else
static void info(const char *fmt,...) {}
#endif

関数ポインタのトピック
libsvmには次のコードがあります.
static void print_string_stdout(const char *s)
{
	fputs(s,stdout);
	fflush(stdout);
}
static void (*svm_print_string) (const char *) = &print_string_stdout;

私はフォーラムで投稿したことがあります.
libsvm代码阅读:svm.cpp浅谈和函数指针_第4张图片
Pumpが毎日学友の返事を学ぶことを见て、私はやっと思い出します:6行目はあれはもとは関数の指针で、ふだん使っていないで、忘れて、教材を出してもう一度学ぶしかありません:
関数ポインタは、オブジェクトではなく関数を指すポインタです.他のポインタと同様に、関数ポインタも特定のタイプを指します.関数タイプは、関数名に関係なく、戻りタイプとパラメータテーブルによって決定されます.
// pf points to function returning bool thar takes two const string references
bool (*pf) (const string &, const string &);

この文はpfを関数を指すポインタとして宣言し、その関数は2つのconst string&タイプのパラメータとboolタイプの戻り値を持つ.
注意:*pfの両側のカッコは必須で、なければ関数ポインタではありません.
// declares a function named pf that returns a bool*
bool *pf (const string &, const string &)

typedef関数ポインタの定義を簡略化
関数ポインタのタイプはかなり冗長です.typedefを使用してポインタタイプに同義語を定義すると、関数ポインタの使用を大幅に簡略化できます.
typedef bool (*cmpFn)(const string &, const string &)

この定義はcmpFnが関数を指すポインタタイプの名前であることを示す.このポインタタイプは、「boolタイプを返し、2つのconst string参照パラメータを持つ関数を返すポインタ」です.このような関数ポインタタイプを使用する場合は、cmpFnを直接使用するだけで、毎回関数タイプ宣言全体を書く必要はありません.
ポインタで関数を呼び出す
関数を指すポインタは、その指す関数を呼び出すために使用できます.解参照オペレータを使用する必要がなく、ポインタで直接関数を呼び出すことができます.
bool lengthCmp(const string&, const string &);
cmpFn pf = lengthCmp;
lengthCmp=("hi","bye");//direct call
pf("hi","bye");//equivalent call: pf1 implicitly dereferenced
(*pf)("hi","bye");//equivalent call: pf1 explicitly dereferenced

Ref:《C++ Primer 4th》pp237-239
本住所:http://blog.csdn.net/linj_m/article/details/19542421
微博:林建民-機械視覚