Golang类型不完整或不可分配问题探讨

Golang类型不完整或不可分配问题探讨 我正在编写一个C++包装器。 假设有以下项目: src.cpp

#include "src.h"
struct SourceType {
        int foo;
};

src.h:

struct SourceType;

c.h 是这个C++代码的C包装器:

typedef struct SourceType SourceType;
struct SourceType;

wrapper.h 是用于GO的包装器:

#pragma once
#include "c.h"

main.go:

package main

/*
#include "wrapper.h"
*/
import "C"

func main() {
	var foo C.SourceType
	_ = foo
}

我遇到了以下错误:

# example.com
./main.go:9:6: _Ctype_struct_SourceType is incomplete (or unallocatable); stack allocation disallowed

更多关于Golang类型不完整或不可分配问题探讨的实战教程也可以访问 https://www.itying.com/category-94-b0.html

5 回复

没关系。我理解你的评论,并且已经成功进行了相同的修改。

更多关于Golang类型不完整或不可分配问题探讨的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


哦,好的,明白了。我无法分配这个对象,我需要分配一个指针。

你好 @gucio321

我在为一个C/C++项目创建Go绑定时遇到了完全相同的问题。能否请你解释一下你所说的“我无法分配这个对象,我需要分配一个指针。”具体是什么意思?我也很想了解如何在我的项目中解决这个问题。谢谢!

这是一个典型的Go与C/C++交互时遇到的类型不完整问题。错误表明Go编译器无法确定C.SourceType类型的大小,因此不能在栈上分配。

问题分析

在您的代码中:

  1. C头文件只声明了SourceType的前向声明(struct SourceType;
  2. Go的cgo需要知道类型的完整定义才能确定其大小
  3. 前向声明对C/C++有效,但对Go的cgo来说类型是不完整的

解决方案

方案1:使用指针代替值类型(推荐)

package main

/*
#include "wrapper.h"
*/
import "C"
import "unsafe"

func main() {
    // 使用指针而不是值类型
    var foo *C.SourceType
    
    // 如果需要分配内存,使用C.malloc
    foo = (*C.SourceType)(C.malloc(C.size_t(unsafe.Sizeof(C.SourceType{}))))
    defer C.free(unsafe.Pointer(foo))
    
    // 或者通过C函数获取指针
    // foo = C.createSourceType()
}

方案2:在C包装器中提供完整类型定义

修改c.h

// c.h
#ifdef __cplusplus
extern "C" {
#endif

// 从C++源文件获取完整定义
typedef struct SourceType SourceType;

// 提供访问函数而不是暴露完整结构
SourceType* createSourceType();
void setFoo(SourceType* st, int value);
int getFoo(SourceType* st);
void destroySourceType(SourceType* st);

#ifdef __cplusplus
}
#endif

方案3:使用不透明指针模式

c.h:

// 不透明指针类型
typedef void* OpaqueSourceType;

OpaqueSourceType createSourceType();
void setFoo(OpaqueSourceType st, int value);
int getFoo(OpaqueSourceType st);
void destroySourceType(OpaqueSourceType st);

main.go:

package main

/*
#include "wrapper.h"
*/
import "C"

func main() {
    // 使用不透明指针
    var foo C.OpaqueSourceType = C.createSourceType()
    defer C.destroySourceType(foo)
    
    C.setFoo(foo, 42)
    value := C.getFoo(foo)
    _ = value
}

方案4:如果必须使用值类型,提供完整定义

修改c.h包含完整定义:

// c.h
#ifdef __cplusplus
extern "C" {
#endif

// 完整结构定义
typedef struct SourceType {
    int foo;
} SourceType;

#ifdef __cplusplus
}
#endif

实际示例

这里是一个完整的工作示例:

src.h:

struct SourceType {
    int foo;
};

c.h:

#ifdef __cplusplus
extern "C" {
#endif

typedef struct SourceType {
    int foo;
} SourceType;

SourceType* createSourceType();
void setFoo(SourceType* st, int value);
int getFoo(SourceType* st);
void destroySourceType(SourceType* st);

#ifdef __cplusplus
}
#endif

wrapper.h:

#pragma once
#include "c.h"

main.go:

package main

/*
#include "wrapper.h"
*/
import "C"
import (
    "fmt"
    "unsafe"
)

func main() {
    // 方法1:通过C函数创建
    foo := C.createSourceType()
    defer C.destroySourceType(foo)
    
    C.setFoo(foo, 100)
    fmt.Printf("Value: %d\n", C.getFoo(foo))
    
    // 方法2:直接分配(需要完整类型定义)
    var bar C.SourceType
    // 通过unsafe.Pointer访问字段
    *(*int32)(unsafe.Pointer(&bar.foo)) = 200
}

关键点

  1. Go的cgo需要知道C类型的完整大小才能在栈上分配
  2. 前向声明对cgo不够,需要完整定义
  3. 使用指针是更安全的方式,特别是当类型可能在不同平台有不同大小时
  4. 不透明指针模式是C/C++与Go交互的常用模式

对于C++包装器,推荐使用方案1或方案3,通过指针和访问函数来操作C++对象,这样可以更好地控制内存生命周期和保持类型安全。

回到顶部