从不同的第三方库中输入两种不带联合的类型

时间:2018-05-12 10:52:41

标签: c++

我已经读过,使用联合进行类型惩罚实际上是C ++中未定义的行为,我想知道你会如何输入双关语呢?

作为一个例子,我使用一个联合来从两个第三方库中输入两种类型,它们具有相同的布局(libAQuaternion和libBQuaternion是那些我无法改变的第三方库的类型):

struct libAQuaternion {
    double x, y, z, w;
};

void libAFunc(libAQuaternion &p) {
    p.x = p.y = p.z = p.w = 1.;
}

struct libBQuaternion {
    double x, y, z, w;
};

void libBFunc(libBQuaternion &p) {
    p.x = p.y = p.z = p.w = 2.;
}

union myQuat {
    libAQuaternion a;
    libBQuaternion b;
};

int main() {
    myQuat q;
    libAFunc(q.a);
    libBFunc(q.b);
}

符合标准的最佳解决方案是什么?

4 个答案:

答案 0 :(得分:3)

  

符合标准的最佳解决方案是什么?

编写一个函数以从一个四元数转换为另一个四元数。

libBQuaternion convert(const libAQuaternion &Quat) {
  return{Quat.x, Quat.y, Quat.z, Quat.w};
}

libAQuaternion convert(const libBQuaternion &Quat) {
  return{Quat.x, Quat.y, Quat.z, Quat.w};
}

// or template if you want to
template<typename T, typename U>
T convertTo(U &&Quat) {
  return{Quat.x, Quat.y, Quat.z, Quat.w};
}

任何优化器都应该能够完全优化它,因此不应该有性能损失。

但如果有一个函数通过lvalue ref获取一个这样的类,那么这将是一个问题。您需要创建相应类的新对象,传递该对象,然后将正确的值重新分配给原始结构。我想你可以为此创建一个函数,但IMO最干净的方法是更改​​函数的签名以通过lvalue ref获取单个值,但这并不总是可行。

如果不调用UB,就无法在C ++中进行类型惩罚。

答案 1 :(得分:2)

C ++不允许打字。大部分时间都是。

你写的内容完全合法,但有一个潜在的危险。

两个四元数为standard layout classescommon initial sequence为{4}}。因此,legal通过联合

读取其他成员
public function invoiceDatatable(Request $request) {
        $raw = Invoice::with('user')->select('invoices.*');
        if ($request->has('customer_id')) {
            $raw->where('invoices.user_id', $request->get('customer_id'));
        }

        $raw->where('invoices.outstanding_price', '>=', '1');

        $datatable = app('datatables')->of($raw)

        ->addColumn('maincheck', function ($raw) {
            return '<label><input type="radio" name="invoice" class="styled_color_checkbox outstanding_price" data-amount="'.$raw->outstanding_price.'" value="'.$raw->id.'"></label>';

        })
        ->editColumn('created_at', function ($raw) {
            return date('Y/m/d', strtotime($raw->created_at));
        })
        ->editColumn('outstanding_price', function ($raw) {
            return '$'.$raw->outstanding_price;
        });

        return $datatable->make(true);

    }

然后我们注意到四元数只能通过内置/普通赋值或新位置来写入。

  • 对非活动成员(或其成员,递归地)使用内置/普通赋值,causes the implicit beginning非活动成员的生命周期。

  • 使用展示位置也将开始会员的生命周期。

因此,在写入四元数时,其生命周期已经开始,或者它将开始。

读取和写入一起称为访问,strict-aliasing rule禁止访问其他类型的内容,这是禁止类型惩罚。但是,由于我们刚刚证明访问四元数是明确定义的,所以这是类型 - 惩罚确实合法的一个例外。

一个危险是当库函数部分写入四元数时

myQuat q = libAQuaternion{1, 0, 0, 0};
std::cout << q.b.x;    // legal

不幸的是,void someFunc(libBQuaternion& q) { q.x = 1; } myQuat q = libAQuaternion{1, 0, 0, 0}; someFunc(q.b); std::cout << q.a.y; // UB 未初始化,因此读取它是未定义的行为。

然而,鉴于所有先前的规则以及未初始化变量背后的原因是效率,编译器不太可能利用UB和“错误编译”。

答案 2 :(得分:1)

template<class D, class S>
D bitcpy( S const* s ){
  static_assert( sizeof(S)>=sizeof(D) );
  D r;
  memcpy( &r, s, sizeof(D) );
  return r;
}
union myQuat {
  libAQuaternion a;
  libBQuaternion b;
  void AsA(){
    a = bitcpy<libAQuaternion>(&b);
  }
  void AsB(){
    b = bitcpy<libBQuaternion>(&a);
  }
};

您必须跟踪联盟中是否有AB。切换来电AsAAsB;这是运行时的noop。

我相信工会规则允许通过转让来激活成员;如果我误解了标准或情况(这些是pod类型吗?)事情变得有点棘手。但我认为这不适用于此。

答案 3 :(得分:0)

最安全的输入方法是使用类型A中的memcpy来输入类型B.在许多平台上memcpy是内在意义,它由编译器实现,因此可能会被优化掉。