C 到 D:结构为类型并初始化?

C to D: struct as type and initialize?

我有这些 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_initks_destroy 中手动处理内存有什么好处吗?

它会出现段错误,因为您没有在堆栈上分配 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。然后需要考虑一些事项,您已经了解其中的大部分内容:

  1. 使用数组代替指针。
  2. 使用 new 而不是 malloc。
  3. 将穷人的模板(宏)转换为 D 模板。
  4. 在结构中移动方法。
  5. 最好将命名风格更改为D Style

代码可能如下所示:

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]);
}

Walter Bright 有一个很棒的主题演讲 Memory DisAllocation - slides,他在其中建议避免 GC 分配并展示了一些这样做的技巧,我推荐它。
然而,我们不能总是避免 GC/heap 分配,如果数组很大,我们必须使用 new.
D 中垃圾收集器的当前实现速度很慢(我们正在摆脱它,很快就不再有 GC),但大多数时候它并没有那么慢,大多数时候你并不需要额外的速度,因此您无需使用 new.

这是一个更 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);


}