8

私のオープンソースのプレーンな C コードでは、次の単純な構造を使用して、文字列バッファーからデータを読み取り、解析します。

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

この単純な 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

このコードは、一部のデータを文字列バッファからカスタム形式でロードするために使用されます。(こちらの方がわかりやすいかもしれません。)


ここで、文字列バッファーからではなく、FILEポインターからデータをロードする必要があります。

実装をコピーして貼り付けるのは嫌いで、代わりに既存のコードを再利用したいと考えています。(もちろん、リファクタリング/適応は問題ありません。)

これは C++ の教科書的なものですが、実行時のオーバーヘッドを発生させずに単純な C でそれを行うにはどうすればよいでしょうか?


APIを使用する関数の例を次に示します。この関数はlts_LoadStateコピー アンド ペーストできません (もちろん、文字列バッファと の両方をサポートするように変更することはできます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;
}
4

3 に答える 3

3

パラメータとして渡す関数変数が必要なようです。C はそれらを実行できますが、構文はあまりきれいではありません。

おそらく実行時のオーバーヘッドが少しありますが、それほど多くはありません。

Cで関数をパラメーターとしてどのように渡しますか?

于 2012-04-03T21:57:33.750 に答える
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...
于 2015-04-11T09:26:31.550 に答える