我有这些C宏,并希望将它们转换为纯D(与原始C文件接口)。
#define __KS_TYPE(type_t) \
typedef struct __kstream_t { \
unsigned char *buf; \
int begin, end, is_eof; \
type_t f; \
} kstream_t;
#define __KS_BASIC(type_t, __bufsize) \
static inline kstream_t *ks_init(type_t f) \
{ \
kstream_t *ks = (kstream_t*)calloc(1, sizeof(kstream_t)); \
ks->f = f; \
ks->buf = (unsigned char*)malloc(__bufsize); \
return ks; \
} \
static inline void ks_destroy(kstream_t *ks) \
{ \
if (ks) { \
free(ks->buf); \
free(ks); \
} \
}
这是我目前的实施:
import std.stdio;
import core.stdc.config;
import core.stdc.stdlib;
struct __kstream_t(type_t) {
ubyte *buf;
int begin, end, is_eof;
type_t f;
}
mixin template __KS_BASIC(type_t, ubyte __bufsize){
// mixin(__KS_TYPE!type_t);
alias kstream_t = __kstream_t!type_t;
static kstream_t *ks_init(type_t f)
{
kstream_t *ks = cast(kstream_t*)calloc(1, kstream_t.sizeof);
ks.f = f;
ks.buf = cast(ubyte*)malloc(__bufsize);
return ks;
}
static void ks_destroy(kstream_t *ks)
{
if (ks) {
free(ks.buf);
free(ks);
writeln("Destroyed struct.");
}
}
}
void main(){
mixin __KS_BASIC!(int, 12);
auto ks = ks_init( 14);
ks.buf[0] = 125;
writeln("ks.f: ", ks.f, ", buf: ", ks.buf[0]);
ks_destroy(ks);
}
D版本当前运行正常,但是我可以对代码进行任何调整和修剪吗?它看起来仍然很C-ism。例如,kh_init
可以是这样的:
static kstream_t *ks_init(type_t f){
kstream_t* ks;
ks.f = f;
return ks;
}
但是这个版本提供了sementation fault
。
此外,在D版本的ks_init
和ks_destroy
中手动处理内存有什么好处吗?
答案 0 :(得分:0)
它是段错误的,因为您没有在堆栈上分配kstream_t
,而是分配一个初始化为null
的指针。
要在堆栈上分配它,你会做:
kstream_t ks; // still bad
但是当您尝试访问其任何字段时,这仍然会在main()
中发生段错误,因为一旦范围退出就会释放堆栈分配,在这种情况下ks_init()
。
你应该在gc上分配它:
auto ks = new kstream_t;
编辑:抱歉,我没有谈论缓冲区的分配,你可以将maclloc内存提供给GC,这样他就可以为你管理它了
ks.buf = cast(ubyte*)malloc(__bufsize);
import core.memory : GC;
GC.addRange(ks.buf, __bufsize);
然而,似乎你有兴趣将该代码移植到惯用的D.然后有一些事情要考虑,你已经找到了大部分:
代码可能如下所示:
import std.stdio;
struct Kstream(T) {
ubyte[] buf;
int begin, end, is_eof;
T f;
this(T f, ubyte bs)
{
this.f = f;
this.buf = new ubyte[bs];
}
~this()
{
writeln("Destroyed struct.");
}
}
void main(){
auto ks = Kstream!int(14, 12);
ks.buf[0] = 125;
writeln("ks.f: ", ks.f, ", buf: ", ks.buf[0]);
}
EDIT2:如果您不想避免GC,可以将缓冲区设为静态数组:
import std.stdio;
struct Kstream(T, size_t N) {
ubyte[N] buf;
int begin, end, is_eof;
T f;
this(T f)
{
this.f = f;
}
~this()
{
// we cannot call writeln in @nogc code
//writeln("Destroyed struct.");
}
}
@nogc
void main(){
auto ks = Kstream!(int, 12)(14);
ks.buf[0] = 125;
// we cannot call writeln in @nogc code
//writeln("ks.f: ", ks.f, ", buf: ", ks.buf[0]);
}
沃尔特·布莱特有一个很棒的主题演讲叫做Memory DisAllocation - slides,在那里他建议避免使用GC分配,并展示了一些技巧,我建议这样做。
但是我们不能总是避免GC /堆分配,如果数组很大,我们必须使用new
D中垃圾收集器的当前实现很慢(并且我们正在逐渐消除它,很快就不再有GC),但是当时它不会那么慢,大部分时间你都不会真的需要额外的速度,所以你不需要使用new
。
答案 1 :(得分:0)
这是一个更D版本,@ DejanLekic的评论和@Sahmi回答有一些启发。我发布了一个答案,因为我不想改变原来的问题。
import std.stdio;
import core.stdc.config;
import core.stdc.stdlib;
struct __kstream_t(type_t) {
ubyte *buf;
int begin, end, is_eof;
type_t f;
this(type_t f, ubyte bs){
this.f = f;
this.buf = cast(ubyte* )malloc(bs);//Can this be avoided or more D?
}
}
mixin template __KS_BASIC(type_t, ubyte __bufsize){
alias kstream_t = __kstream_t!type_t;
static kstream_t* ks_init(type_t f){
auto ks = new kstream_t(f, __bufsize);
return ks;
}
static void ks_destroy(kstream_t *ks)
{
if (ks) {
destroy(ks);
writeln("Destroyed struct.");
}
}
}
void main(){
mixin __KS_BASIC!(int, 12);
auto ks = ks_init( 14);
ks.buf[0] = 125;
writeln("ks.f: ", ks.f, ", buf: ", ks.buf[0]);
ks_destroy(ks);
}