web-dev-qa-db-ja.com

Objective-CからC ++でバイナリファイルを浮動小数点の多次元配列に読み込む

次のコードをObjective CからC++に変換したいと思います。

クラスmyClassには、次の属性があります。

float tab[dim1][dim2][dim3];

客観的Cファイルでは、多次元配列はバイナリファイルから入力されます。

NSData *dataTab=[NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"pathOfMyTab" ofType:@""]];
[dataTab getBytes:myClass -> tab  length:[dataTab length]];

どうすればこの部分をC++に変換できますか?

4
PatriceG

私はあなたのファイルに配列のバイト表現が含まれていると仮定しています。この場合、C++のみを使用してObjective-Cコードの動作を模倣します(このC++を作成するのはreinterpret_cast<>のみです。それ以外の場合は単なるCです)。次のコードを使用できます。 。エラーチェックは追加していませんが、実行したい箇所にコメントを残しています。

float tab[dim1][dim2][dim3];

CFBundleRef mainBundle = CFBundleGetMainBundle();
CFURLRef dataTabURL = CFBundleCopyResourceURL(mainBundle, CFSTR("pathOfMyTab"), NULL, NULL);

CFReadStreamRef stream = CFReadStreamCreateWithFile(NULL, dataTabURL); // check for NULL return value
CFReadStreamOpen(stream);                                              // check for errors here
CFReadStreamRead(stream, reinterpret_cast<UInt8 *>(tab), sizeof tab);  // check that this function returns the number of bytes you were expecting (sizeof tab)
CFReadStreamClose(stream);

// we own "stream" and "dataTabURL" because we obtained these through functions
// with "create" in the name, therefore we must relinquish ownership with CFRelease
CFRelease(stream);
CFRelease(dataTabURL); // ditto

std::stringで使用可能なパスがすでにある場合は、次のC++コードを使用してObjective-Cコードの動作を模倣できます。

// make sure to include this header
#include <fstream>

// ... then elsewhere in your .cpp file ...
float tab[dim1][dim2][dim3];

std::string path = "path/to/mytab"; // obtain from somewhere
std::ifstream input(path, std::ios::binary); // check that the file was successfully opened
input.read(reinterpret_cast<char *>(tab), sizeof tab); // check that input.gcount() is the number of bytes you expected

この場合、reinterpret_cast<>を使用する必要があると思います。これは、ファイルに配列の実際の表現が含まれているためです(以前に同様の方法でファイルに書き込まれたと想定)。

ハイブリッドアプローチを使用できます。リソースへのパスを含むCFURLRefを取得すると、 this function を使用してURLのファイルシステム表現を取得できます(適切なサイズの出力を提供します)結果を保存するためのバッファ)、そしてそこからstd::ifstreamのコンストラクタの1つに渡すことができるはずです(ただし、適切なタイプにキャストする必要がある場合もあります)。

C++は可変長配列をサポートしていません(配列のサイズはコンパイル時に既知でなければなりません)。また、標準ライブラリによって提供されるマトリックスタイプもないため、実行時にテーブルの次元が変化する場合は、私の答えにあるものとは完全に別のアプローチが必要になります。 Objective-Cからの出力のシリアル化(JSONなどの形式を使用)を検討して、マトリックスの次元も出力に書き込まれるようにして、C++でファイルを解析しやすくすることができます。

1
dreamlax

私の考えでは、最も簡単で最も速い方法は、memcpy()を使用して、NSDataのバイトを、ソース配列と同じ構造(次元)を持つターゲット配列にコピーすることです。たとえば、次を参照してください。

https://github.com/Voldemarus/MultiDimensionalArrayDemo/tree/master

#import "DemoClass.h"

#define DIM1    3
#define DIM2    4
#define DIM3    2

@interface DemoClass() {
    int src[DIM1][DIM2][DIM3];  // source (initial) array

    int dst[DIM1][DIM2][DIM3];  // destination array
}
@end

@implementation DemoClass

- (instancetype) init
{
    if (self = [super init]) {
        for (int i = 0; i < DIM1; i++) {
            for (int j = 0; j < DIM2; j++) {
                for (int k = 0; k < DIM3; k++) {
                    int value = i*100 + j*10 + k;
                    src[i][j][k] = value;
                }
            }
        }
    }
    return self;
}

int getIntFromArray(int *array, int i, int j, int k) {
    int offset = j*DIM3 + i*DIM2*DIM3;
    return array[offset];
}

void putIntToArray(int *array, int i, int j, int k, int value) {
    int offset = j*DIM3 + i*DIM2*DIM3;
    array[offset] = value;
}

- (void) run
{
    // Step 1. Save array into NSData
    NSInteger s = sizeof(int)*DIM1*DIM2*DIM3;
    NSData *data = [[NSData alloc] initWithBytes:src length:s];
    NSAssert(data, @"NSData should be created");
    //Step2 - Create new array
    int *bytes = (int *)[data bytes];
    memcpy(dst,bytes,s);
    // Step 3. Compare src and dst
    for (int i = 0; i < DIM1; i++) {
        for (int j = 0; j < DIM2; j++) {
            for (int k = 0; k < DIM3; k++) {
                int template = i*100 + j*10 + k;
                int s = src[i][j][k];
                int d = dst[i][j][k];
 //               NSLog(@"i %d j %d k %d -->s = %d  d = %d",i,j,k,s,d);
                NSAssert(s == template, @"Source array should have value from template");
                NSAssert(d == s, @"Destination array should be identical to the source");
            }
        }
    }

}

@end
1

float tab[dim1][dim2][dim3]は3次元配列のように見えます。標準の実装では、3つのネストされたFORループを使用します。

したがって、C++の実装は次のようになります。

  • 読んだ dim1dim2dim3どこかから、通常はファイルの最初の値(たとえば、12バイト、各数値に4バイト)
  • 3つのネストされた[〜#〜] for [〜#〜]ループでファイルの残りを読み取る

何かのようなもの:

    for (size_t i = 0; i < dim1; ++i) 
       for (size_t j = 0; j < dim2; ++j)
         for (size_t k = 0; k < dim3; ++k)
           tab[i][j][k] = read_float_value(inputFile);

Objective-Cでは、同様の方法でファイルを書き込むことができます。

ここにあなたが始めるためのいくつかの例があります:

0
Cosmin