Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions doc/en/dev/llcppg.md
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,89 @@ struct struct2 {
};
```

##### Nested Enum

Similar to nested structs, nested enums can also be accessed in the global scope. llcppg handles named nested enums by creating separate type declarations that are accessible globally.

###### Anonymous Nested Enum

Anonymous nested enums are converted to inline enum constants within the parent struct context, with the enum values defaulting to `c.Int` type.

```c
typedef struct Config {
int version;
enum {
MODE_DEBUG = 0,
MODE_RELEASE = 1
} mode;
} Config;
```

**Generated Go code**:
```go
type Config struct {
Version c.Int
Mode c.Int
}

const (
MODE_DEBUG c.Int = 0
MODE_RELEASE c.Int = 1
)
```
Comment on lines +222 to +247
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this binding actually loss some semantics.
in c,we can know the mode field at struct Config only support twe method for input (eg MODE_DEBUG , MODE_RELEASE ), but this llgo binding lost this context , this Config type will mean support every int 's input.will cause confuse and not safe operate.

so i think the better way to keep the context is like the anonmousy struct

llcppg/doc/en/dev/llcppg.md

Lines 155 to 175 in 7abb9e8

###### Anonymous Nested Struct
Anonymous nested structs/unions are converted to inline Go struct types within the parent struct.
```c
struct outer {
struct {
int x;
int y;
} inner;
};
```
```go
type Outer struct {
Inner struct {
X c.Int
Y c.Int
}
}
```

but unfortunately go have not enum,so i think here need a type to keep this context

Copy link
Contributor Author

@MeteorsLiu MeteorsLiu Aug 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should not add a type, because in common case, we don't use a type for an anonymous enum.
We should respect the source, not add an Intermediate type. If we add an Intermediate type, that will cause user confused instead of the words you said.

For example,

struct Config {
    int version;
    enum {
        MODE_DEBUG = 0,
        MODE_RELEASE = 1
    } mode;
};

in C, we just use it like,

struct Config cfg;
cfg.mode = MODE_DEBUG;

If we add a type, just we call it NestedMode,

type Config struct {
    version int
     mode NestedMode
}
type NestedMode c.Int 
const (
        MODE_DEBUG NestedMode = 0,
        MODE_RELEASE NestedMode = 1
)

var cfg Config
cfg.mode = MODE_DEBUG

It looks great, but user may think it "What's NestedMode, what's that?"

If this user wrote C but gonna to rewrite using LLGo, he will assert MODE_DEBUG is c.Int not NestedMode. When he's going to use this enum, he found the type is totally wrong and might think it's a bug for llcppg, because in his mind, he only think c.Int is the correct type of MODE_DEBUG. This situation, we call it subconsciousness psychologically.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And to add an Intermediate type, we need to consider more, like duplicated type name or how to rename it, it's meaningless that we do that "convert" thing.

Copy link
Contributor Author

@MeteorsLiu MeteorsLiu Aug 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And enum value is unique, usually. The enum itself is a unique symbol, just like the type.

In rust, you can see that enum code

    enum IpAddr {
        V4(String),
        V6(String),
    }

    let home = IpAddr::V4(String::from("127.0.0.1"));

    let loopback = IpAddr::V6(String::from("::1"));

Copy link
Contributor Author

@MeteorsLiu MeteorsLiu Aug 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And this code shows that, in C, the type of a enum field is c.Int

#include <stdio.h>

struct a {
	enum b {A} k;	
};

int main()
{
	struct a aa;
	aa.k = 2;

   printf("%d", aa.k);
   
   return 0;
}

Output 2, but 2 is not the member of enum b.

And sizeof(aa.k) is 4, same as c.Int

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think for process these anonymous type need with consist process . at #449 we confirm process the anonymous func type with a intermediate type, this decision also base the same problem.

  1. llgo can't direct use anonymous func type.
  2. need throw type binding info to user.

so i think we need make them consistent.

additional gogen current not support field doc. goplus/gogen#455

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#449 hasn't been merged, so it can't be a conclusion.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but it's also a consensus for anonymous type

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on the meeting discussion, both methods we discussed are expected, and we reached a consensus that llcppg ultimately aims to convert C libraries into a user-friendly interface presented as LLGo Bindings, providing as much original information as possible. However, considering the current input-output ratio, we will first implement the current design version to provide basic conversion capabilities, and then separately consider providing this unified intermediate type to make the user interface friendly.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So i think we need note this design in the future will have a alias type is better


###### Named Nested Enum

Named nested enums in C are accessible in the global scope, not just within the context of the outer struct. llcppg handles this by creating separate enum type declarations.

**Reason**: In C, named nested enums are declared in the global scope and can be used independently. This means the enum type can be used anywhere in the code, not just within the context of the outer struct.

**NOTE:** Should we add type alias here in the future? See discussion: [#530](https://github.com/goplus/llcppg/pull/530)

```c
typedef struct Config {
int version;
enum LogLevel {
LOG_DEBUG = 0,
LOG_INFO = 1,
LOG_ERROR = 2
} level;
} Config;

// This is valid C - LogLevel is in global scope
struct Config cfg;
cfg.level = LOG_INFO;
```

**Generated Go code**:
```go
type LogLevel c.Int

const (
LOG_DEBUG LogLevel = 0
LOG_INFO LogLevel = 1
LOG_ERROR LogLevel = 2
)

type Config struct {
Version c.Int
Level LogLevel
}
```

This is equivalent to:
```c
enum LogLevel {
LOG_DEBUG = 0,
LOG_INFO = 1,
LOG_ERROR = 2
};
struct Config {
int version;
enum LogLevel level;
};
```

##### Function

###### To Normal Function
Expand Down
Loading