[疑难] 请教opCall
hurd
2009-07-21
下面程序为什么会断言失败?
extern(C) int memcpy(void*, void*, int); struct ArrayX(int L){ ubyte[L] _Buf; uint _Offset; alias ArrayX!(L) SelfType; SelfType opCall (void[] x){ int _offset = x.length + _Offset; if( _offset < L ){ memcpy(&_Buf[_Offset], &x[0], x.length); _Offset = _offset; } return *this; } char[] str(){ assert( _Offset < L ); return cast(char[]) _Buf[ 0 .. _Offset]; } } final class ArrayZ(int L){ ubyte[L] _Buf; uint _Offset; alias ArrayZ!(L) SelfType; final SelfType opCall (void[] x){ int _offset = x.length + _Offset; if( _offset < L ){ memcpy(&_Buf[_Offset], &x[0], x.length); _Offset = _offset; } return this; } char[] str(){ assert( _Offset < L ); return cast(char[]) _Buf[ 0 .. _Offset]; } } void main(){ ArrayX!(512) x; x("1")("2"); scope z = new ArrayZ!(512) ; z("1")("2"); assert( x.str == z.str ); } 另外请教x的成员数据是不是都在栈上, z的情况是不是也一样? |
|
Colorful
2009-07-21
按照D语言规范,x,z 都在堆栈上。
ArrayZ 的初始化应该没有调用 opCall(),这个重载只适用于 struct ,想要在 class 中改变内存初始化,可以重载 new 。 以上完全根据 D 语言规范猜测,还没有测试过。 PS : 在 D2 中 opCall 现在完全可以在 struct 中使用 this() 来代替,这样 struct 和 class 就有了一个统一的构造函数了,呵呵。 |
|
hurd
2009-07-21
ArrayX!(512) x; x("1")("2")("3")("4")("5"); assert(x.str == "1"); // 断言成功 上面是为什么呢? |
|
lifc
2009-07-21
刚才没发成功,再来一次~
说来巧了,昨天和今天才提交了两个dmd 2.031的bug,上午移植一段在stack上构建临时packet的代码遇到很相似的问题,差点就当bug提交上去了。还好后来用自带的obj2asm看了一下汇编代码才找到问题。 因为每次调用ArrayZ的opCall都会返回一个新临时对象,第二次调用是针对第一次的临时对象来操作,调用结果被丢弃了,所以得不到你预期的结果。如果把前面的x("1")("2")放到后面,改成assert(x("1")("2").str == z.str)结果就正确了,但这种做法效率不怎样。 |
|
lifc
2009-07-22
今天想了一下我的代码,从楼主的opCall问题引申出来一个新问题。为了不产生上面问题只好返回结构指针,D虽然不像C++一样要用"."和"->"区别对待对象和对象指针的成员访问,但似乎对值类型的操作符重载仍旧持有双重标准:
// 值类型,为简化没写成stack分配模板 struct ByteBuffer { static ByteBuffer opCall (int size) { ByteBuffer ret; ret.buffer = new ubyte[size]; return ret; } // 为了避免楼主的问题,必须写成返回结构指针 ByteBuffer* opShl (ubyte val) { buffer[ptr++] = val; return this; } ByteBuffer* put (ubyte val) { buffer[ptr++] = val; return this; } uint ptr; ubyte[] buffer; } struct MyMessage { // ByteBuffer的opShl非侵入式写法,可以不改变ByteBuffer实现增加新参数类型 ByteBuffer* opShl_r (ref ByteBuffer bb) { bb.put(1); return &bb; } } void main () { ByteBuffer bb = ByteBuffer(64); bb << 0x55; bb << 0xaa; assert(bb.ptr == 2); //bb << 0x55 << 0xaa; // 编译出错,因为opShl返回的是ByteBuffer*,只能改为下面形式 bb.put(0x55).put(0xaa); // <<操作符重载改成put可以通过,但下面的用法就没办法了 MyMessage msg; //bb << msg << 2; // 出错,原因同上,不知如何回避 *(bb << msg) << 2; // 可以通过,但这种用法很别扭 assert(bb.ptr == 6); } 请教一下上面的代码在不改变ByteBuffer值类型(struct)前提下能否用<<操作符重载思路比较流畅的表达出来? |
|
lifc
2009-07-22
上官方论坛转了一圈,原来类似问题2006年就有人提出http://www.digitalmars.com/d/archives/digitalmars/D/dtl/540.html,可惜和很多建议贴一样没了下文。D社群很多有心有力的热心群众出主意难采纳(有些建议过了4、5年才进入2.0测试),想帮忙又插不上手,时间久了大家的热情也就淡了...
|