C到D: struct作为类型和初始化?

时间:2022-10-11 21:26:05

I have these C macros and want to convert them to pure D (as apposed to interfacing with the original C file).

我有这些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); \
        } \
    }

This is my current implementation:

这是我现在的实现:

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


}

The D version current runs fine, but can I make any tuning and trimming of the code? It still looks very C-ism. For example, kh_init can just be like this:

D版本当前运行良好,但是我可以对代码进行调整和调整吗?它看起来仍然很像c主义。例如,kh_init可以是这样的:

static kstream_t *ks_init(type_t f){
    kstream_t* ks;
    ks.f = f;
    return ks;
  }

But this version give a sementation fault.

但是这个版本有一个错误。

Additionally, is there any benefit of handling memory manually in ks_init and ks_destroy in the D version?

另外,在D版本的ks_init和ks_destroy中手动处理内存有什么好处吗?

2 个解决方案

#1


0  

It segfaults because you are not allocating a kstream_t on the stack, instead you are allocating a pointer initialized to null. to allocate it on the stack you would do:

因为您没有在堆栈上分配kstream_t,而是将一个指针初始化为null。把它分配到栈上,你可以这样做:

kstream_t ks;  // still bad

but this would still segfault in main() when you try to access any of its fields because stack allocations are freed as soon as the scope exits which is in this case ks_init().

但是,当您试图访问它的任何字段时,这仍然是segfault,因为在这个例子中,堆栈分配会立即释放,在这个例子中是ks_init()。

you should instead allocate it on the gc:

你应该把它分配给gc:

auto ks = new kstream_t;

EDIT: Sorry, I didn't talk about the allocation for the buffer, you could give the maclloc'd memory to the GC so he can manage it for you

编辑:对不起,我没有讨论缓冲区的分配,你可以把maclloc的内存给GC,这样他就可以为你管理。

ks.buf = cast(ubyte*)malloc(__bufsize);
import core.memory : GC;
GC.addRange(ks.buf, __bufsize);

However it seems you are interested in porting that code to idiomatic D. Then there a few things to consider, you already figured out most of them:

然而,你似乎有兴趣将这段代码移植到惯用的d。然后,有一些事情需要考虑,你已经找到了其中的大部分:

  1. use arrays instead of pointers.
  2. 使用数组代替指针。
  3. use new instead of malloc.
  4. 用新的代替malloc。
  5. convert poor man's templates (macros), into D templates.
  6. 将穷人的模板(宏)转换为D模板。
  7. move methods inside struct.
  8. 移动内部结构的方法。
  9. preferably change naming style to D Style.
  10. 最好更改命名风格为D风格。

The code might look like:

代码可能是这样的:

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: if you wan't to avoid the GC you could make the buffer a static array:

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

There is a great keynote by Walter Bright called Memory DisAllocation - slides, where he recommended avoiding GC allocations and showed some techniques to do so, I recommend it.
However we cannot always avoid GC/heap allocations, if an array is large we have to use new.
The current implementation of the Garbage Collector in D is slow (and we're getting rid of it, soon no more GC), though must of the time it isn't that slow, most of the time you do not really need that extra speed, so you need not feer using new.

Walter Bright有一个很好的主题叫做内存分配-幻灯片,他建议避免GC分配,并展示了一些技巧,我推荐它。然而,我们不能总是避免GC/堆分配,如果数组很大,我们必须使用new。当前在D中的垃圾收集器的实现是缓慢的(我们正在处理它,很快就不再有GC了),尽管它的速度不是很慢,大多数时候您并不需要额外的速度,所以您不必使用新的。

#2


0  

This is a more D version, with some inspriation from @DejanLekic's comment and @Sahmi answer. I am posting an answer because I don't want to change the original question.

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


}

#1


0  

It segfaults because you are not allocating a kstream_t on the stack, instead you are allocating a pointer initialized to null. to allocate it on the stack you would do:

因为您没有在堆栈上分配kstream_t,而是将一个指针初始化为null。把它分配到栈上,你可以这样做:

kstream_t ks;  // still bad

but this would still segfault in main() when you try to access any of its fields because stack allocations are freed as soon as the scope exits which is in this case ks_init().

但是,当您试图访问它的任何字段时,这仍然是segfault,因为在这个例子中,堆栈分配会立即释放,在这个例子中是ks_init()。

you should instead allocate it on the gc:

你应该把它分配给gc:

auto ks = new kstream_t;

EDIT: Sorry, I didn't talk about the allocation for the buffer, you could give the maclloc'd memory to the GC so he can manage it for you

编辑:对不起,我没有讨论缓冲区的分配,你可以把maclloc的内存给GC,这样他就可以为你管理。

ks.buf = cast(ubyte*)malloc(__bufsize);
import core.memory : GC;
GC.addRange(ks.buf, __bufsize);

However it seems you are interested in porting that code to idiomatic D. Then there a few things to consider, you already figured out most of them:

然而,你似乎有兴趣将这段代码移植到惯用的d。然后,有一些事情需要考虑,你已经找到了其中的大部分:

  1. use arrays instead of pointers.
  2. 使用数组代替指针。
  3. use new instead of malloc.
  4. 用新的代替malloc。
  5. convert poor man's templates (macros), into D templates.
  6. 将穷人的模板(宏)转换为D模板。
  7. move methods inside struct.
  8. 移动内部结构的方法。
  9. preferably change naming style to D Style.
  10. 最好更改命名风格为D风格。

The code might look like:

代码可能是这样的:

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: if you wan't to avoid the GC you could make the buffer a static array:

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

There is a great keynote by Walter Bright called Memory DisAllocation - slides, where he recommended avoiding GC allocations and showed some techniques to do so, I recommend it.
However we cannot always avoid GC/heap allocations, if an array is large we have to use new.
The current implementation of the Garbage Collector in D is slow (and we're getting rid of it, soon no more GC), though must of the time it isn't that slow, most of the time you do not really need that extra speed, so you need not feer using new.

Walter Bright有一个很好的主题叫做内存分配-幻灯片,他建议避免GC分配,并展示了一些技巧,我推荐它。然而,我们不能总是避免GC/堆分配,如果数组很大,我们必须使用new。当前在D中的垃圾收集器的实现是缓慢的(我们正在处理它,很快就不再有GC了),尽管它的速度不是很慢,大多数时候您并不需要额外的速度,所以您不必使用新的。

#2


0  

This is a more D version, with some inspriation from @DejanLekic's comment and @Sahmi answer. I am posting an answer because I don't want to change the original question.

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


}