プログラミング言語における反射メカニズム
5056 ワード
今日、開発の過程で2つの類似製品のコードを統合し、コードのメンテナンス量を減らし、同時に品目のメンテナンスコストを減らす必要があります.2つの製品はただ1つの信用構造体内の要素にすぎず、構造体の同じ要素の安いアドレスが変化し、同時にいくつかの要素が増加した.設計の初めにアップグレードの問題を考慮すべきで、通信アドレスの互換性をやり遂げて、元の通信アドレスを変えないべきで、しかし前人がすでに設計したため、しかも工事が膨大で、更に修正しにくいです.これに基づいて,プログラムにどの構造体を用いて使用するかを動的に決定させ,ちょうど,この構造体の一定の安価な量のうち,どの製品であるかを区別するために1つの製品IDを用いることができることを考えた.これはちょうど反射メカニズムで、ここで反射メカニズムを記録して勉強します.
はんしゃきこう
コンピュータの反射は、実行中に私がチェックし、内部メンバーを操作します.つまり、この変数のタイプは動的に変化することができ、実行時にその役割を確定することができ、多くの高度なプログラミング言語には、Python、lua、c#、javaがこのメカニズムを持っています.次にluaを例に
変数のタイプが指定されていないため、任意のタイプを彼に割り当て、実行時に現在のタイプを検出することができます.
c反射機構の実現
高度な言語の反射メカニズムは、簡単に言えば文字列型で対応するクラスや関数を取得することができます.しかし、c言語にはこのメカニズムがなく、自分で実現する必要がある.
基礎形式、c言語構造化プログラミング基礎実現
1)宣言
2)呼び出し
この方法の不便な点は、マッピングが必要な関数が異なるファイルに分散している場合、新しいマッピングを追加するたびにこの配列、およびヘッダファイルを修正する必要があることである.
カスタムセグメントの使用
gccサポート使用_attribute__((section())、関数、変数を指定したデータセグメントに配置します.すなわち,コンパイラに1で配列にメンバーを追加する動作を完了させることができる.このメカニズムにより、コールバック関数は、他のファイルを変更する必要がなく、任意のファイルで宣言できます.
カーネル駆動初期化関数の遍歴実行には,この方法が採用されている.次はinit/mainです.c初期化関数に対する遍歴呼び出し
arch/xxx/kernel/vmlinuxを組み合わせます.lds
リンクスクリプトを使用して、各セグメントの開始アドレスを取得し、各初期化関数を1つのセグメントで巡回して実行します.
はんしゃきこう
コンピュータの反射は、実行中に私がチェックし、内部メンバーを操作します.つまり、この変数のタイプは動的に変化することができ、実行時にその役割を確定することができ、多くの高度なプログラミング言語には、Python、lua、c#、javaがこのメカニズムを持っています.次にluaを例に
local AllTypes = {
Type1 = 1,
Type2 = 2,
Type3 = 3,
}
local typeClsHash = {}
typeClsHash[AllTypes.Type1] = Cls1
typeClsHash[AllTypes.Type2] = Cls2
typeClsHash[AllTypes.Type3] = Cls3
local theType = AllTypes.Type2
local cls = typeClsHash[theType]
local instance = cls:new()
instance:doSth()
変数のタイプが指定されていないため、任意のタイプを彼に割り当て、実行時に現在のタイプを検出することができます.
c反射機構の実現
高度な言語の反射メカニズムは、簡単に言えば文字列型で対応するクラスや関数を取得することができます.しかし、c言語にはこのメカニズムがなく、自分で実現する必要がある.
基礎形式、c言語構造化プログラミング基礎実現
1)宣言
typedef void (*callback)(void);
typedef struct {
const char *name;
callback fn;
}callback_t;
void f0();
void f1();
callback_t callbacks[] = {
{ "cmd0", f0},
{"cmd1", f1},
}
void f0()
{
}
void f1()
{
}
2)呼び出し
void do_callback(const char *name)
{
size_t i;
for (i = 0; i < sizeof(callbacks) / sizeof(callbacks[0]); i++) {
if (!strcmp(callbacks[i].name, name)) {
return callbacks[i].fn();
}
}
}
この方法の不便な点は、マッピングが必要な関数が異なるファイルに分散している場合、新しいマッピングを追加するたびにこの配列、およびヘッダファイルを修正する必要があることである.
カスタムセグメントの使用
gccサポート使用_attribute__((section())、関数、変数を指定したデータセグメントに配置します.すなわち,コンパイラに1で配列にメンバーを追加する動作を完了させることができる.このメカニズムにより、コールバック関数は、他のファイルを変更する必要がなく、任意のファイルで宣言できます.
カーネル駆動初期化関数の遍歴実行には,この方法が採用されている.次はinit/mainです.c初期化関数に対する遍歴呼び出し
extern initcall_t __initcall_start[];
extern initcall_t __initcall0_start[];
extern initcall_t __initcall1_start[];
extern initcall_t __initcall2_start[];
extern initcall_t __initcall3_start[];
extern initcall_t __initcall4_start[];
extern initcall_t __initcall5_start[];
extern initcall_t __initcall6_start[];
extern initcall_t __initcall7_start[];
extern initcall_t __initcall_end[];
static initcall_t *initcall_levels[] __initdata = {
__initcall0_start,
__initcall1_start,
__initcall2_start,
__initcall3_start,
__initcall4_start,
__initcall5_start,
__initcall6_start,
__initcall7_start,
__initcall_end,
}; // lds
/* Keep these in sync with initcalls in include/linux/init.h */
static char *initcall_level_names[] __initdata = {
"early",
"core",
"postcore",
"arch",
"subsys",
"fs",
"device",
"late",
};
static void __init do_initcall_level(int level)
{
extern const struct kernel_param __start___param[], __stop___param[];
initcall_t *fn;
strcpy(static_command_line, saved_command_line);
parse_args(initcall_level_names[level],
static_command_line, __start___param,
__stop___param - __start___param,
level, level,
&repair_env_string);
for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++)
do_one_initcall(*fn);
}
static void __init do_initcalls(void)
{
int level;
for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)
do_initcall_level(level);
}
arch/xxx/kernel/vmlinuxを組み合わせます.lds
. = ALIGN(4096); /* Init code and data */
__init_begin = .;
. = ALIGN(4096);
.init.text : AT(ADDR(.init.text) - 0)
{
_sinittext = .;
*(.init.text) *(.cpuinit.text) *(.meminit.text) _einittext = .;
}
.init.data : AT(ADDR(.init.data) - 0)
{
*(.init.data) *(.cpuinit.data) *(.meminit.data) *(.init.rodata) *(.cpuinit.rodata) *(.meminit.rodata) . = ALIGN(32);
__dtb_start = .;
*(.dtb.init.rodata) __dtb_end = .;
. = ALIGN(16);
__setup_start = .;
*(.init.setup) __setup_end = .;
__initcall_start = .;
*(.initcallearly.init)
__initcall0_start = .;
*(.initcall0.init) *(.initcall0s.init)
__initcall1_start = .;
*(.initcall1.init) *(.initcall1s.init)
__initcall2_start = .;
*(.initcall2.init) *(.initcall2s.init)
__initcall3_start = .;
*(.initcall3.init) *(.initcall3s.init)
__initcall4_start = .;
*(.initcall4.init) *(.initcall4s.init)
__initcall5_start = .;
*(.initcall5.init) *(.initcall5s.init)
__initcallrootfs_start = .;
*(.initcallrootfs.init) *(.initcallrootfss.init)
__initcall6_start = .;
*(.initcall6.init) *(.initcall6s.init)
__initcall7_start = .;
*(.initcall7.init) *(.initcall7s.init)
__initcall_end = .;
__con_initcall_start = .;
*(.con_initcall.init)
__con_initcall_end = .;
__security_initcall_start = .;
*(.security_initcall.init)
__security_initcall_end = .; }
リンクスクリプトを使用して、各セグメントの開始アドレスを取得し、各初期化関数を1つのセグメントで巡回して実行します.