问题
I have these C macros and want to convert them to pure D (as apposed to interfacing with the original C file).
#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:
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?
回答1:
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 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()
.
you should instead allocate it on the 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
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:
- use arrays instead of pointers.
- use new instead of malloc.
- convert poor man's templates (macros), into D templates.
- move methods inside struct.
- preferably change naming style to D Style.
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:
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
.
回答2:
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.
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);
}
来源:https://stackoverflow.com/questions/43354980/c-to-d-struct-as-type-and-initialize