[疑难] 请教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测试),想帮忙又插不上手,时间久了大家的热情也就淡了...
Global site tag (gtag.js) - Google Analytics