将C代码移植到C ++,将void *从malloc转换为所需的指针的问题 [英] Porting C code to C++, problem with casting void* from malloc to desired pointer

查看:48
本文介绍了将C代码移植到C ++,将void *从malloc转换为所需的指针的问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前正在移植一些我写给C ++的C代码以供娱乐.我在用C进行的 malloc()调用中苦苦挣扎,出于简单的原因, h w 是常量,但后来与运行时交换了常数:

  double(* g2)[h] [w] = malloc(h * w * sizeof(double)); 

在C语言中,这是 void * 的隐式转换,当然,这在C ++中不起作用.

我已经尝试使用 reinterpret_cast< double [h] [w]> 强制转换,但这仍然是无效的转换.

我想知道如何才能用C ++进行这项工作,因为这样可以节省很多工作?

作为替代方案,我可能会使用具有间接性的矩阵类:

  struct Matrix:std :: vector< double>{无符号的matSize;std :: vector< double *>间接Matrix():matSize(0){}Matrix(unsigned n):matSize(n){调整大小(n * n);indirection.resize(n);for(unsigned i = 0; i< n; ++ i){indirection [i] =&(**)[i * n];}}双重&operator()(无符号i,无符号j){返回间接[i] [j];}const double&运算符()(无符号i,无符号j)const {返回间接[i] [j];}}; 

解决方案

移植不仅仅使它逐行起作用,所以:

C:

  double(* g2)[h] [w] = malloc(h * w * sizeof(double));...g2 [y] [x] = ...; 

C ++:

  std :: vector< double>g2(h * w);...g2 [y + x * h] = ...;//或者g2 [y * w + x] = ...; 

使用语法不方便访问元素,因此您可能希望将其包装在一个简单的类中.示例:

  #include< iostream>#include< iterator>#include< vector>类arr2d {上市:arr2d(size_t h,size_t w):data_(h * w),w_(w){}内联double&运算符()(size_t y,size_t x){返回data_ [y * w_ + x];}内联双操作符()(size_t y,size_t x)const {返回data_ [y * w_ + x];}//获取指向行的指针内联double *运算符[](size_t y){返回& data_ [y * w_];}内联双精度const *运算符[](size_t y)const {返回& data_ [y * w_];}内联size_t width()const {return w_;}私人的:std :: vector< double>数据_;size_t w_;};int main(){arr2d g2(3,4);g2(2,3)= 3.14159;//替代访问:g2 [1] [2] = 1.23456;std :: cout<<g2 [2] [3]<<"\ n";double *行= g2 [2];std :: copy(row,row + g2.width(),std :: ostream_iterator< double>(std :: cout,,")));std :: cout<<"\ n";} 

输出:

  3.141590、0、0、3.14159, 

非初始化版本可能如下:

  class arr2d {上市:arr2d(size_t h,size_t w):data_(new double [w * h]),w_(w){}内联Double&operator()(size_t y,size_t x){return data_ [y * w_ + x];}内联双操作符()(size_t y,size_t x)const {return data_ [y * w_ + x];}内联double *运算符[](size_t y){return&_data_ [y * w_];}内联双精度const *运算符[](size_t y)const {return&_data_ [y * w_];}内联size_t width()const {return w_;}私人的:std :: unique_ptr< double []>数据_;size_t w_;}; 

但是请注意,
std :: copy(row,row + g2.width(),std :: ostream_iterator< double>(std :: cout,``,``)));
从第一个示例开始将导致不确定的行为.

还请注意,此版本将删除复制构造函数和复制分配运算符.如果需要,必须自己实施.

非初始化版本的创建时间当然很难与任何初始化版本相提并论,但是对于访问时间,人们可能会认为查找表或您所谓的间接表会加快行速与一次性完成乘法和加法相比.

我的结果:
8x8 http://quick-bench.com/f8zcnU9P8oKwMUwLRXYKZnLtcLM
1024x1024 http://quick-bench.com/0B2rQeUkl-WoqGeG-iS1hdP4ah8
4096x4096 http://quick-bench.com/c_pGFmB2C9_B3r3aRl7cDK6BlxU

似乎有所不同.对于4096x4096矩阵,查找版本速度更快,但是对于两个较小的矩阵,原始版本的查找速度更快.您需要比较接近将要使用的大小,并检查不同的编译器.有时我会与赢家"完全相反更改编译器时.

由于您不介意从 std :: vector 继承或为查找表保留额外的数据,因此可以选择.似乎比其他版本略胜一筹.

  class arr2d:受保护的std :: vector< double *>{上市:使用std :: vector< double *> :: operator [];//行"来自基类的访问器arr2d(size_t h,size_t w):std :: vector< double *>(h),data_(new double [w * h]),w_(w),h_(h){for(size_t y = 0; y< h; ++ y)(* this)[y] =& data_ [y * w];}内联size_t width()const {return w_;}内联size_t height()const {return h_;}私人的:std :: unique_ptr< double []>数据_;size_t w_,h_;}; 

以下是Philipp-P(OP:s)自己针对不同2D阵列实现的测量结果:

8x8 http://quick-bench.com/vMS6a9F_KrUf97acWltjV5CFhLY
1024x1024 http://quick-bench.com/A8a2UKyHaiGMCrf3uranwOCwmkA
4096x4096 http://quick-bench.com/XmYQc0kAUWU23V3Go0Lucioi_Rg

>

相同版本的5点模板代码的结果:
8x8 http://quick-bench.com/in_ZQTbbhur0I4mu-NIquT4c0ew
1024x1024 http://quick-bench.com/tULLumHZeCmC0HUSfED2K4nEGG8
4096x4096 http://quick-bench.com/_MRNRZ03Favx91-5IXnxGNpRNwQ

I am currently porting some C code I wrote to C++ for fun. I am struggling with a malloc() call I make in C, with h and w being constants for simplicity reasons, but later exchanged with runtime constants:

double (*g2)[h][w] = malloc(h * w * sizeof(double));

In C, this is an implicit conversion of a void*, and this of course doesn't fly with C++.

I already tried casting this with reinterpret_cast<double[h][w]>, but this is still an invalid cast.

I was wondering, how can I make this work in C++ since this would save me a lot of work?

As an alternative I'll probably use a matrix class with indirection:

struct Matrix : std::vector<double> {
    unsigned matSize;
    std::vector<double*> indirection;
    Matrix() : matSize(0) {}
    Matrix(unsigned n) : matSize(n) {
        resize(n*n);
        indirection.resize(n);
        for(unsigned i = 0; i < n; ++i) {
            indirection[i] = &(*this)[i*n];
        }
    }
    double& operator()(unsigned i, unsigned j) {
        return indirection[i][j];
    }
    const double& operator()(unsigned i, unsigned j) const {
        return indirection[i][j];
    }
};

解决方案

Porting involves more than just making it work, line by line, so:

C:

double (*g2)[h][w] = malloc(h * w * sizeof(double));
...
g2[y][x] = ...;

C++:

std::vector<double> g2(h*w);
...
g2[y+x*h] = ...; // or
g2[y*w+x] = ...;

Using that syntax is inconvenient for accessing elements so you might want to wrap it inside a simple class. Example:

#include <iostream>
#include <iterator>
#include <vector>

class arr2d {
public:
    arr2d(size_t h, size_t w) : data_(h * w), w_(w) {}

    inline double& operator()(size_t y, size_t x) { 
        return data_[y * w_ + x];
    }
    inline double operator()(size_t y, size_t x) const {
        return data_[y * w_ + x];
    }

    // getting pointer to a row
    inline double* operator[](size_t y) {
        return &data_[y * w_];
    }
    inline double const* operator[](size_t y) const {
        return &data_[y * w_];
    }

    inline size_t width() const { return w_; }

private:
    std::vector<double> data_;
    size_t w_;
};

int main() {
    arr2d g2(3, 4);

    g2(2, 3) = 3.14159;
    // alternative access:
    g2[1][2] = 1.23456;

    std::cout << g2[2][3] << "\n";

    double* row = g2[2];
    std::copy(row, row + g2.width(), std::ostream_iterator<double>(std::cout, ", "));
    std::cout << "\n";
}

Output:

3.14159
0, 0, 0, 3.14159,

A non-initializing version could look like:

class arr2d {
public:
    arr2d(size_t h, size_t w) : data_(new double[w * h]), w_(w) {}

    inline double& operator()(size_t y, size_t x) { return data_[y * w_ + x]; }
    inline double operator()(size_t y, size_t x) const { return data_[y * w_ + x]; }

    inline double* operator[](size_t y) { return &data_[y * w_]; }
    inline double const* operator[](size_t y) const { return &data_[y * w_]; }

    inline size_t width() const { return w_; }

private:
    std::unique_ptr<double[]> data_;
    size_t w_;
};

But note that the
std::copy(row, row + g2.width(), std::ostream_iterator<double>(std::cout, ", "));
from the first example would lead to undefined behaviour.

Also note that this version will delete the copy constructor and copy assignment operator. You'll have to implement them yourself if you need them.

The creation time for the non-initializing version is of course hard to beat with any initializing version, but for access times, one might think that a lookup table, or indirection as you call it, for the rows would speed up things compared to doing the multiplication and addition in one go.

My results:
8x8 http://quick-bench.com/f8zcnU9P8oKwMUwLRXYKZnLtcLM
1024x1024 http://quick-bench.com/0B2rQeUkl-WoqGeG-iS1hdP4ah8
4096x4096 http://quick-bench.com/c_pGFmB2C9_B3r3aRl7cDK6BlxU

It seems to vary. The lookup version is faster for the 4096x4096 matrix but the naive version is faster for the two smaller ones. You need to compare using sizes close to what you'll be using and also check with different compilers. I sometimes get completely opposite "winners" when changing compiler.

Since you don't mind inheriting from std::vector or keeping extra data for a lookup-table, this could be an option. It seems to outperform the other versions slightly.

class arr2d : protected std::vector<double*> {
public:
    using std::vector<double*>::operator[]; // "row" accessor from base class

    arr2d(size_t h, size_t w) :
        std::vector<double*>(h), 
        data_(new double[w * h]), 
        w_(w),
        h_(h)
    {
        for(size_t y = 0; y < h; ++y)
            (*this)[y] = &data_[y * w];
    }

    inline size_t width() const { return w_; }
    inline size_t height() const { return h_; }

private:
    std::unique_ptr<double[]> data_;
    size_t w_, h_;
};

Here are Philipp-P's (OP:s) own measurements for the different 2D-array implementations:

8x8 http://quick-bench.com/vMS6a9F_KrUf97acWltjV5CFhLY
1024x1024 http://quick-bench.com/A8a2UKyHaiGMCrf3uranwOCwmkA
4096x4096 http://quick-bench.com/XmYQc0kAUWU23V3Go0Lucioi_Rg

Results for 5-point stencil code for the same versions:
8x8 http://quick-bench.com/in_ZQTbbhur0I4mu-NIquT4c0ew
1024x1024 http://quick-bench.com/tULLumHZeCmC0HUSfED2K4nEGG8
4096x4096 http://quick-bench.com/_MRNRZ03Favx91-5IXnxGNpRNwQ

这篇关于将C代码移植到C ++,将void *从malloc转换为所需的指针的问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆