7

私がベクトルクラスを持っているとしましょう:

typedef struct vec3_s
{
    float x, y, z;
}
vec3;

ただし、floatの配列に変換せずに、反復処理できるようにしたいと思います。この場合、キャストは許容されますが、C ++のような機能がストレートCで実行できるかどうかを知りたいです。たとえば、C ++では、std::vector< T >添え字[]演算子がオーバーロードされているため、そのアドレスを渡すことができます。をとる関数への最初のインデックスvoid*

すなわち、

void do_something_with_pointer_to_mem( void* mem )
{
    // do stuff
}

int main( void )
{
    std::vector< float > v;

    // fill v with values here

    // pass to do_something_with_pointer_to_mem

    do_some_with_pointer_to_mem( &v[ 0 ] );

    return;
}

もう1つのより具体的な例は、OpenGLでglBufferData(...)の呼び出しが行われる場合(C ++を使用する場合)です。

glBufferData( GL_ARRAY_BUFFER, sizeof( somevector ), &somevector[ 0 ], GL_STREAM_DRAW );

それで、添え字演算子を使用してCで同様のことを達成することは可能ですか?そうでない場合で、関数(たとえば)を作成する必要があった場合、それが定義されているヘッダーファイルにそれfloat vec3_value_at( unsigned int i )だけを含めるのは理にかなっていますか?static inline

4

3 に答える 3

26

すべての構造体フィールドが同じタイプの場合、次のように共用体を使用できます。

typedef union vec3_u
{
    struct vec3_s {
        float x, y, z;
    };
    float vect3_a[3];
}
vec3;

このようにして、各x、y、またはzフィールドに個別にアクセスするか、vect3_a配列を使用してそれらを反復処理することができます。このソリューションは、メモリや計算の面で何の費用もかかりませんが、C++のようなソリューションからは少し遠いかもしれません。

于 2013-01-19T20:49:16.817 に答える
4

C ++のシンタックスシュガーは得られませんが、C++で作成する関数をoperator[]として作成するのは簡単です。

float get_vec3(v *vec3, int i) {
   switch(i) {
   case 0: return v->x;
   case 1: return v->y;
   case 2: return v->z;
   }
   assert(0);
 }

これで、任意のvec3を反復処理できます。

 for (int i = 0; i < 3; i++) {
     printf("%f\n", get_vec3(v, i));
 }
于 2013-01-19T20:42:41.550 に答える
2

あなたがやろうとしていることに関するCの問題は、構造を移動する方法を知る必要があることです(つまり、型を知る必要があります)。std::vector<T>テンプレート(C++概念)を使用しているため、そのように機能する理由があります。そうは言っても、あなたが提案したものとは少し違うことを試すことができます。配列を使用したくない場合は、ジェネリック型を格納できます。ただし、データを取得して使用する場合、ユーザーは自分が期待しているデータの種類を知っている必要があります。以下では、配列を回避し(ただし、配列を使用することでよりクリーンなソリューションが存在する可能性があります)、ほぼ同じ柔軟性を提供するリンクリストの実装がありますstd::vector<T>(これはリンクリストであるため、パフォーマンス上の利点は別として)O(n)すべての操作(リストを巧妙に逆にして、O(1)挿入することもできますが、これは単なる例です)

#include <stdio.h>
#include <stdlib.h>
typedef struct _item3_t
{
  void *x, *y, *z;
  struct _item3_t* next;
} item3_t;

typedef struct
{
  item3_t* head;
} vec3_t;

void insert_vec3(vec3_t* vec, void* x, void* y, void* z)
{
  item3_t* item = NULL;
  item3_t* tmp  = NULL;
  int i = 0;
  if(vec == NULL)
    return;

  item = malloc(sizeof(item3_t));
  item->x = x;
  item->y = y;
  item->z = z;
  item->next = NULL;

  tmp = vec->head;
  if(tmp == NULL) { // First element
    vec->head = item;
  } else {
    while(tmp->next != NULL)
      tmp = item->next;
    tmp->next = item;
  }
}

// This is one method which simply relies on the generic method above
void insert_vec3_float(vec3_t* vec, float x, float y, float z)
{
  float* xv, *yv, *zv;
  if(vec == NULL)
    return;
  xv = malloc(sizeof(float));
  yv = malloc(sizeof(float));
  zv = malloc(sizeof(float));

  *xv = x;
  *yv = y;
  *zv = z;

  insert_vec3(vec, xv, yv, zv);
}

void init_vec3(vec3_t* vec)
{
  if(vec == NULL)
    return;
  vec->head = NULL;
}

void destroy_vec3(vec3_t* vec)
{
  item3_t* item = NULL, *next = NULL;
  if(vec == NULL)
    return;

  item = vec->head;
  while(item != NULL) {
    next = item->next;
    free(item->x);
    free(item->y);
    free(item->z);
    free(item);
    item = next;
  }
}

item3_t* vec3_get(vec3_t* vec, int idx)
{
  int i = 0;
  item3_t* item = NULL;
  if(vec == NULL)
    return NULL;
  item = vec->head;
  for(i = 0 ; i < idx && item != NULL ; ++i)
    item = item->next;
  return item;
}

void do_something(item3_t* item)
{
  if(item == NULL)
    return;
  float x = *((float*)item->x);
  float y = *((float*)item->y);
  float z = *((float*)item->z);

  // To do - something? Note, to manipulate the actual
  // values in the vector, you need to modify their values
  // at their mem addresses
}

int main()
{
  vec3_t vector;

  init_vec3(&vector);

  insert_vec3_float(&vector, 1.2, 2.3, 3.4);

  printf("%f %f %f\n", *((float*)vec3_get(&vector, 0)->x), *((float*)vec3_get(&vector, 0)->y), *((float*)vec3_get(&vector, 0)->z));

  do_something(vec3_get(&vector, 0));

  destroy_vec3(&vector);

  return 0;
}

このコードは、箱から出してすぐにコンパイルする必要があります。ここにあるのは、「ベクトル」(特にvec3構造)であるリンクリストです。リスト内の各ノード(つまり、あるstd::vector<T>意味ではすべての要素)には、すべてvoidポインターである3つの要素があります。したがって、ここに任意のデータ型を格納できます。唯一の落とし穴は、それらのポインタが指すようにメモリを割り当てる必要があることです。要素を削除するときは、そのメモリを解放する必要があります(vec3_destroy例についてはメソッドを参照してください)。これが、これらのvoidポインタがあなたのケースでどのように機能するかを理解するのにもう少し役立つことを願っています。

データを取得するために[]表記を使用することはできませんがvec3_get、同じ方法でメソッドを使用できます。このdo_somethingメソッドは、OPで述べたものと同様のことを達成できる可能性のある何らかの方法のスタブの例です。

于 2013-01-19T21:15:07.877 に答える