2012-04-03 43 views
7

my open-source plain C code我使用這個簡單的結構來讀出並從字符串緩衝區中解析數據:應用鴨打字在純C

typedef struct lts_LoadState 
{ 
    const unsigned char * pos; 
    size_t unread; 
} lts_LoadState; 

該緩衝區由this simple API訪問:

/* Initialize buffer */ 
void ltsLS_init(lts_LoadState * ls,const unsigned char * data, size_t len); 

/* Do we have something to read? (Actually a macro.) */ 
BOOL ltsLS_good(ls); 

/* How much do we have to read? (Actually a macro.) */ 
size_t ltsLS_unread(ls); 

/* Eat given number of characters, return pointer to beginning of eaten data */ 
const unsigned char * ltsLS_eat(lts_LoadState * ls, size_t len); 

注:ltsLS_unread可以替換爲return (ltsLS_good(ls)) ? SIZE_MAX : 0而不會破壞當前的實現。

此代碼用於load some data in a custom format from a string buffer。 (This可以是更好的說明。)


現在我需要負載數據未從字符串緩衝區,但是從FILE指針。

我討厭複製粘貼實現,而想重複使用現有的代碼。 (當然,我可以重構/修改它。)

這是C++中的教科書,但是如何在純C中做到這一點,而不會產生運行時開銷?


這裏是使用lts_LoadState API的示例功能,這是不被複制粘貼(但可被改變,當然,以支持字符串緩衝區和FILE *):

static int ltsLS_readline(
    lts_LoadState * ls, 
    const unsigned char ** dest, 
    size_t * len 
) 
{ 
    const unsigned char * origin = ls->pos; 
    unsigned char last = 0; 
    size_t read = 0; 

    while (ltsLS_good(ls)) 
    { 
    if (ltsLS_unread(ls) > 0) 
    { 
     unsigned char b = *ls->pos; /* OK, this should be ltsLS_eat_char macro. */ 
     ++ls->pos; 
     --ls->unread; 

     if (b == '\n') 
     { 
     *dest = origin; 
     *len = (last == '\r') ? read - 1 : read; 

     return LUATEXTS_ESUCCESS; 
     } 

     last = b; 
     ++read; 
    } 
    else 
    { 
     ls->unread = 0; 
     ls->pos = NULL; 
    } 
    } 

    return LUATEXTS_ECLIPPED; 
} 
+0

我正在考慮用某些腳本生成C代碼,因此「複製粘貼」會自動完成。這可能是最簡單的方法。但是我正在尋找沒有代碼生成的「正確」解決方案(當然,預處理器是OK的)。 – 2012-04-03 21:51:50

+0

我結束了使用內存映射文件 - 這樣我可以重複使用完全相同的'lts_LoadState'及其訪問函數。 – 2012-04-04 20:25:40

回答

3

這聽起來像你想要的功能變量,你會作爲參數傳遞。 C可以做到,但是語法不是很漂亮。

可能會有一些運行時間開銷,但不是很多。

How do you pass a function as a parameter in C?

+0

嗯,我不確定在這種情況下運行時開銷是否可以忽略不計 - 除非GCC在玩智能遊戲......當然這很有可能。但這很容易進行基準測試。無論如何:有沒有辦法做到我想要的沒有函數指針,但也許,一些預處理器代碼? – 2012-04-03 22:04:54

+0

我確定有,雖然CPP宏可能會有點長。另外,如果你用宏而不是函數指針來實現,宏可能不會更快,特別是當你來回跳轉很多時 - 因爲宏可能會溢出CPU緩存。 – user1277476 2012-04-04 00:05:31

+0

我無法真正想象一下C預處理宏溢出CPU緩存的意思。 – 2015-05-24 01:10:13

2

我討厭打開這個備份,但是這是我在想今天我不認爲這有很大的答案。

我認爲要實現在C中的鴨子打字你以後是一個全球性的vtable。每個結構體(對象)都應該將vtable作爲結構中的第一個元素。基本上,只要有想要通過鴨子打字訪問的行爲,就可以將其添加到此全局vtable中;那麼無論傳遞給函數的對象是什麼,都可以調用它,您可以將對象轉換爲表格,查看行爲應該在的位置,檢查它是否爲非null,然後調用它。

//Would be declared in some global.h or similar 
struct global_v_table_t = 
{ 
    char* (*toString)(void); 
    //... other functions being accessed through duck typing go here 
} 

//-------------------- 
//In some other files: 
//Then we create some objects: 
struct bicycle_t 
{ 
    struct global_v_table; 
    void (*ride)(void); 
}; 

//When we initialise a bicycle 
bicycle_t * bicycyle_init(void) 
{ 
    bicycle_t * bike = malloc(sizeof(bicycle_t)); 
    //Req'd basically for every object within the project: 
    //Either do this or call calloc() instead of malloc(): 
    globalVtableInit((global_v_table_init)bike);//NULL the vtable 
    //Set the behaviours that this object exhibits: 
    bike->global_v_table.toString = BIKE_toString;  
} 


static char * bikeString = "I'm a bike!"; 
char * BIKE_toString(void) 
{ 
    return bikeString; 
} 

//---------------- 

//Now anyone can ask that an object provide it's toString: 

//The example uses an error logging function: 

void logError(void * obj) 
{ 
    char * (toStringMethod)(void) = ((global_v_table *)obj)->toString; 
    if (NULL != toStringMethod) 
    {//As long as the object implements the toString behaviour: 
     printf(toStringMethod()); //Print the object's toString. 
    } 
} 

//Will tidy this code up a bit later but this is what I'm thinking. 
//Hopefully is at least partly understandable. The obvious drawback 
//to this implementation is that for every object you get this massive 
//v_table which is full of mostly NULL's for each object as it scales. 
//If you want to make C behave like other languages though you have 
//to expect some sort of penalty I guess... 
0

我曾在我的token操作PostScript解釋同樣需要工作是否相同從FILE*或字符串讀取。看起來,您已經完成了第一步,至少部分是通過get/unget對將解析邏輯與數據訪問分開。如果可以編寫與庫FILE*函數的原型相匹配的字符串版本,則可以簡化實現。

對於我來說,我有一個主要的入口點,它爲get/unget訪問器提供函數指針。

int toke (Xpost_Context *ctx, 
     Xpost_Object *src, 
     int (*next)(Xpost_Context *ctx, Xpost_Object *src), 
     void (*back)(Xpost_Context *ctx, int c, Xpost_Object *src), 
     Xpost_Object *retval); 

正常的操作符執行處理根據類型調用適當的接口函數。因此文件版本調用toke並以較低級別的術語執行這兩個操作。

/* file token token true 
       false 
    read token from file */ 
static 
int Fnext(Xpost_Context *ctx, 
      Xpost_Object *F) 
{ 
    return xpost_file_getc(xpost_file_get_file_pointer(ctx->lo, *F)); 
} 
static 
void Fback(Xpost_Context *ctx, 
      int c, 
      Xpost_Object *F) 
{ 
    (void)ungetc(c, xpost_file_get_file_pointer(ctx->lo, *F)); 
} 
static 
int Ftoken (Xpost_Context *ctx, 
      Xpost_Object F) 
{ 
    Xpost_Object t; 
    int ret; 
    if (!xpost_file_get_status(ctx->lo, F)) 
     return ioerror; 
    ret = toke(ctx, &F, Fnext, Fback, &t); 
    if (ret) 
     return ret; 
    if (xpost_object_get_type(t) != nulltype) { 
     xpost_stack_push(ctx->lo, ctx->os, t); 
     xpost_stack_push(ctx->lo, ctx->os, xpost_bool_cons(1)); 
    } else { 
     xpost_stack_push(ctx->lo, ctx->os, xpost_bool_cons(0)); 
    } 
    return 0; 
} 

而字符串版本使用兩個操作的字符串實現。

/* string token substring token true 
        false 
    read token from string */ 
static 
int Snext(Xpost_Context *ctx, 
      Xpost_Object *S) 
{ 
    int ret; 
    if (S->comp_.sz == 0) return EOF; 
    ret = xpost_string_get_pointer(ctx, *S)[0]; 
    ++S->comp_.off; 
    --S->comp_.sz; 
    return ret; 
} 
static 
void Sback(Xpost_Context *ctx, 
      int c, 
      Xpost_Object *S) 
{ 
    --S->comp_.off; 
    ++S->comp_.sz; 
    xpost_string_get_pointer(ctx, *S)[0] = c; 
} 
static 
int Stoken (Xpost_Context *ctx, 
      Xpost_Object S) 
{ 
    Xpost_Object t; 
    int ret; 

    ret = toke(ctx, &S, Snext, Sback, &t); 
    if (ret) 
     return ret; 
    if (xpost_object_get_type(t) != nulltype) { 
     xpost_stack_push(ctx->lo, ctx->os, S); 
     xpost_stack_push(ctx->lo, ctx->os, t); 
     xpost_stack_push(ctx->lo, ctx->os, xpost_bool_cons(1)); 
    } else { 
     xpost_stack_push(ctx->lo, ctx->os, xpost_bool_cons(0)); 
    } 
    return 0; 
} 

這是從文件的src/LIB/xpost_op_token.c的xpost postscript interpreter