定义

接口封装,将方法转换为函数的过程

过程

类型对应

C语言类型 CGO类型 Go语言类型
char C.char byte
singed char C.schar int8
unsigned char C.uchar uint8
short C.short int16
unsigned short C.ushort uint16
int C.int int32
unsigned int C.uint / unsigned int uint32
long C.long int32
unsigned long C.ulong uint32
long long int C.longlong int64
unsigned long long int C.ulonglong uint64
float C.float float32
double C.double float64
size_t C.size_t uint
bool C._Bool bool
struct xx C.struct_xx type xx struct
char * *C.char string
void * *C.void unsafe.Pointer

准备

|- c                   # c 代码
|- CTPv6.6.8_20220712  # c++ 官方接口
|- generater           # go 生成器代码
| - go |
| ---- | def              # go 类型结构体定义 
| ---- | demo             # go 示例代码 
| ---- | lib              # c 封装编译的库文件 
| ---- | quote            # 行情 
| ---- | trade            # 交易

[外链图片转存失败,源站可能有防盗链机制,建议图片保存下来直接上传(img-zQ7U0UE2-1676773667608)(null)]

调用函数

C++⬅️C⬅️cgo⬅️golang

C++导出函数

  • 函数定义

$ nm -g -C thostmduserapi_se.so |grep CreateFtdcMdApi
00000000001de0f0 T CThostFtdcMdApi::CreateFtdcMdApi(char const*, bool, bool)
$ nm -g thostmduserapi_se.so |grep CreateFtdcMdApi
00000000001de0f0 T _ZN15CThostFtdcMdApi15CreateFtdcMdApiEPKcbb

  • go 中调用
package main

import (
 "fmt"
 "os"
)

// 第一步: 定义函数
/*
// thostmduserapi_se 文件名须有 lib 前缀
// cp thostmduserapi_se.so libthostmduserapi_se.so
#cgo linux LDFLAGS: -fPIC -L${SRCDIR} -Wl,-rpath ${SRCDIR} -lthostmduserapi_se -lstdc++

//void* _ZN15CThostFtdcMdApi15CreateFtdcMdApiEPKcbb(char const*, bool, bool);
// bool 定义为 _Bool
void* _ZN15CThostFtdcMdApi15CreateFtdcMdApiEPKcbb(char const*, _Bool, _Bool);

#include <stdlib.h>
#include <stdint.h>
*/
import "C" // 第二步: 引入 cgo

func main() {
 path := C.CString("./log/")
 os.MkdirAll("./log/", os.ModePerm)
 // 第三步: 使用 cgo 调用
 var api unsafe.Pointer = C._ZN15CThostFtdcMdApi15CreateFtdcMdApiEPKcbb(path, false, false)
 fmt.Println(api)
}

C 导出函数

C++ 方法变为 C 导出函数

  • C++

virtual void RegisterFront(char *pszFrontAddress) = 0;
virtual void Init() = 0;

  • C
#define DLL_EXPORT extern "C"
#include "../CTPv6.6.8_20220712/ThostFtdcMdApi.h"
#include <iostream>
using namespace std;

DLL_EXPORT void RegisterFront(CThostFtdcMdApi *api, char *pszFrontAddress){
    cout << pszFrontAddress << endl;
    return api->RegisterFront(pszFrontAddress);
}

DLL_EXPORT void Init(CThostFtdcMdApi *api, char *pszFrontAddress){
    cout << "init" << endl;
    return api->Init();
}
echo 生成 libctpquote.so 到 go/lib
cd "$( dirname "${BASH_SOURCE[0]}" )"/go/lib
g++ -shared -fPIC -Wl,-rpath . -o libctpquote.so ../../c/quote.cpp  thostmduserapi_se.so 

/*
void RegisterFront(void*, char*);
*/

func main()
...
 front := C.CString("tcp://180.168.146.187:10131")
 C.RegisterFront(api, front)
    C.Init(api)
...

C.CString: string → *C.char
C.GoString:*C.char → string

C++导出函数 → C

为了接口易用性, 将 C++ 导出函数封装成 C 导出函数

  • C
DLL_EXPORT void* CreateFtdcMdApi(const char *pszFlowPath = "", const bool bIsUsingUdp=false, const bool bIsMulticast=false){
    cout << pszFlowPath << endl;
    return CThostFtdcMdApi::CreateFtdcMdApi(pszFlowPath, bIsUsingUdp, bIsMulticast);
}
  • go

// #cgo linux LDFLAGS: -fPIC -L${SRCDIR} -Wl,-rpath ${SRCDIR} -lthostmduserapi_se -lctpquote -lstdc++
# cgo linux LDFLAGS: -fPIC -L${SRCDIR} -Wl,-rpath ${SRCDIR} -lctpquote -lstdc++

// void*_ZN15CThostFtdcMdApi15CreateFtdcMdApiEPKcbb(char const*, _Bool,_Bool);
void*CreateFtdcMdApi(char const*, _Bool,_Bool);
...
// api := C._ZN15CThostFtdcMdApi15CreateFtdcMdApiEPKcbb(path, false, false)
api := C.CreateFtdcMdApi(path, false, false)

响应函数

C++➡️C➡️cgo➡️golang

OnFrontConnected

  • C++
virtual void OnFrontConnected(){};
  • C

# include "../CTPv6.6.8_20220712/ThostFtdcMdApi.h"

class Quote: CThostFtdcMdSpi{
public:
    // 定义响应函数类型
    typedef void OnFrontConnectedType();
    // 声明响应函数指针变量
    void *_OnFrontConnected;
    // 调用函数指针变量
    virtual void OnFrontConnected(){
        if (_OnFrontConnected) { ((OnFrontConnectedType*)_OnFrontConnected)(); }
    }
};

// #include "../CTPv6.6.8_20220712/ThostFtdcMdApi.h"
#include "quote.h"
...
// 创建 Quote 实例
DLL_EXPORT void* CreateFtdcMdSpi() {
    return new Quote(); 
}

// 注册 Quote 实例给 Api
DLL_EXPORT void RegisterSpi(CThostFtdcMdApi *api, CThostFtdcMdSpi *spi) {
    api->RegisterSpi(spi);
}

// 用 Set 函数将 go 函数指针赋值给 C 函数指针
DLL_EXPORT void SetOnFrontConnected(Quote *spi, void *onFunc){
    spi->_OnFrontConnected = onFunc;
}
  • Cgo

/*
...
void* CreateFtdcMdSpi();
void RegisterSpi(void *, void*);
// 1
void exOnFrontConnected();
// 3
void SetOnFrontConnected(void *, void*);
...
*/

// 2
//export exOnFrontConnected
func exOnFrontConnected() {
 fmt.Println("行情接口连接")
}

经过 1 2 go函数转换为C的函数指针, 3 将此指针传递给 C

  • go
func main(){
 ...  
 var spi unsafe.Pointer = C.CreateFtdcMdSpi()
 C.RegisterSpi(api, spi)

 C.SetOnFrontConnected(spi, C.exOnFrontConnected)

 front := C.CString("tcp://180.168.146.187:10131")
 C.RegisterFront(api, front)
  ...
}

OnRspUserLogin

  • C++

virtual void OnRspUserLogin(CThostFtdcRspUserLoginField *pRspUserLogin, CThostFtdcRspInfoField*pRspInfo, int nRequestID, bool bIsLast) {};

  • C
    typedef void OnRspUserLoginType(CThostFtdcRspUserLoginField *pRspUserLogin, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast);
    void *_OnRspUserLogin;
    virtual void OnRspUserLogin(CThostFtdcRspUserLoginField *pRspUserLogin, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast) {
        if (_OnRspUserLogin){
            ((OnRspUserLoginType *)_OnRspUserLogin)(pRspUserLogin, pRspInfo, nRequestID, bIsLast);
        }
    };
DLL_EXPORT int ReqUserLogin(CThostFtdcMdApi *api, CThostFtdcReqUserLoginField *pReqUserLoginField, int nRequestID){
    return api->ReqUserLogin(pReqUserLoginField, nRequestID);
}

DLL_EXPORT void SetOnRspUserLogin(Quote *spi, void *onFunc){
    spi->_OnRspUserLogin = onFunc;
}
  • Cgo

$ nm -g -C ../lib/libctpquote.so |grep OnRspUserLogin
0000000000001a4a W CThostFtdcMdSpi::OnRspUserLogin(CThostFtdcRspUserLoginField*, CThostFtdcRspInfoField*, int, bool)

/*
// 导入接口原类型声明
#cgo CPPFLAGS: -fPIC -I../../CTPv6.6.8_20220712
#include "ThostFtdcUserApiDataType.h"
#include "ThostFtdcUserApiStruct.h"
...
// virtual int ReqUserLogin(CThostFtdcReqUserLoginField *pReqUserLoginField, int nRequestID)
int ReqUserLogin(void *api, struct CThostFtdcReqUserLoginField *pReqUserLoginField, int nRequestID);

void SetOnRspUserLogin(void *, void *);

// virtual void OnRspUserLogin(CThostFtdcRspUserLoginField *pRspUserLogin, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast)
void exOnRspUserLogin(struct CThostFtdcRspUserLoginField*, struct CThostFtdcRspInfoField*, int, _Bool);
*/

//export exOnRspUserLogin
func exOnRspUserLogin(loginField *C.struct_CThostFtdcRspUserLoginField, rspInfo *C.struct_CThostFtdcRspInfoField, requestID C.int, isLast C._Bool) {
 type CThostFtdcRspInfoField struct {
  ErrorID  int32
  ErrorMsg [81]byte
 }
 info := (*CThostFtdcRspInfoField)(unsafe.Pointer(rspInfo))
 fmt.Println("登录: ", info.ErrorID, string(info.ErrorMsg[:]))
}
  • cgo 声明

    CThostFtdcReqUserLoginField → struct CThostFtdcReqUserLoginField

  • Cgo → go

    rspInfo *C.struct_CThostFtdcRspInfoField → (*CThostFtdcRspInfoField)(unsafe.Pointer(rspInfo))

  • go


func main(){
...  
 C.SetOnRspUserLogin(spi, C.exOnRspUserLogin)
...
 type CThostFtdcReqUserLoginField struct {
  // 交易日
  TradingDay [9]byte
  // 经纪公司代码
  BrokerID [11]byte
  // 用户代码
  UserID [13]byte
  // 密码
  Password [41]byte
  // 用户端产品信息
  UserProductInfo [11]byte
  // 接口端产品信息
  InterfaceProductInfo [11]byte
  // 协议信息
  ProtocolInfo [11]byte
  // Mac地址
  MacAddress [21]byte
  // 动态密码
  OneTimePassword [41]byte
  // 保留的无效字段
  reserve1 [16]byte
  // 登录备注
  LoginRemark [36]byte
  // 终端IP端口
  ClientIPPort int32
  // 终端IP地址
  ClientIPAddress [33]byte
 }
    f := CThostFtdcReqUserLoginField{}
 copy(f.BrokerID[:], "9999")
 copy(f.UserID[:], "008105")
 copy(f.Password[:], "1")

 nRequestID := C.int(1)
 C.ReqUserLogin(api, (*C.struct_CThostFtdcReqUserLoginField)(unsafe.Pointer(&amp;f)), nRequestID)
...
}

  • go → Cgo

    f CThostFtdcReqUserLoginField → (*C.struct_CThostFtdcReqUserLoginField)(unsafe.Pointer(&amp;f))

接口封装

正则表达式制作生成器
template 生成文件 (插件 gotemplate-syntax 高亮)
- 清除 template 标志产生的换行(开始标志放在后,结算标记放在前,中间标志放两边)
[[ if -]] [[- else -]] [[- end ]]

datatype.go

ThostFtdcUserApiDataType.h → datatype.go

  • 示例
/
///TFtdcExchangePropertyType是一个交易属性类型
/
///正常
#define THOST_FTDC_EXP_Normal '0'
///根据成交生成报单
#define THOST_FTDC_EXP_GenOrderByTrade '1'

typedef char TThostFtdcExchangePropertyType;
// 交易属性类型
type TThostFtdcExchangePropertyType byte
const THOST_FTDC_EXP_Normal TThostFtdcExchangePropertyType = '0' // 正常
const THOST_FTDC_EXP_GenOrderByTrade TThostFtdcExchangePropertyType = '1' // 根据成交生成报单
  • 正则

    • /+.+是一个(.+)n[/n]*(///[^;]+)?typedefs+(w+)s+(w+)(?:[(d+)])? → // $1ntype $4 [$5]byte

    • ///s*(.*)n#defines+(w+)s+‘(.+)’ → const $2 = $3 // $1

  • 模板


type Typedef struct {
 Name    string
 Type    string
 Length  int
 Comment string
 Define  []struct {
  Var     string
  Value   string
  Comment string
 }
}

[[ range $index, $typedef := .]]// [[ .Comment ]]
type [[ .Name ]] [[ toGo .Type .Length ]]
[[ range .Define ]]const [[ .Var ]] [[ $typedef.Name ]]  = [[ if eq (len .Value) 1 ]]'[[ .Value ]]'[[ else ]]"[[ .Value ]]"[[ end ]] // [[ .Comment ]]
[[ end ]]
[[ end ]]

模板中 $ = Global, 在循环引用全局变量: $.globalVar, 全局变量数组时,需要 range 得到子项.

struct.go

ThostFtdcUserApiStruct.h → struct.go

  • 示例

///响应信息
struct CThostFtdcRspInfoField
{
 ///错误代码
 TThostFtdcErrorIDType ErrorID;
 ///错误信息
 TThostFtdcErrorMsgType ErrorMsg;
};

// 信息分发
type CThostFtdcRspInfoField struct {
    // 错误代码
 ErrorID TThostFtdcErrorIDType
    // 错误信息
 ErrorMsg TThostFtdcErrorMsgType
}
  • 正则

    • ///s*(S*)sstructs+(w+)s{([^}]*)} → // $1ntype $2 struct {}

    • ///(S*)s*(w+)s+([^;]+) → // $1n$3 $2

  • 模板


type Struct struct {
  Name    string
  Comment string
  Fields  []struct {
   Name    string
   Type    string
   Comment string
  }
 }

[[ range .]]// [[ .Comment ]]
type [[ .Name ]] struct {
    [[ range .Fields ]]// [[ .Comment ]]
 [[ .Name ]] [[ .Type]]
    [[ end ]]
}
[[ end ]]

quote.h quote.cpp

ThostFtdcMdApi.h → quote.h quote.cpp

示例
  • 调用函数

///用户登录请求
virtual int ReqUserLogin(CThostFtdcReqUserLoginField *pReqUserLoginField, int nRequestID) = 0;

// 用户登录请求
DLL_EXPORT int ReqUserLogin(CThostFtdcMdApi *api, CThostFtdcReqUserLoginField *pReqUserLoginField, int nRequestID){
    cout << "ReqUserLogin" << endl;
    return api->ReqUserLogin(pReqUserLoginField, nRequestID);
}

///登录请求响应
virtual void OnRspUserLogin(CThostFtdcRspUserLoginField *pRspUserLogin, CThostFtdcRspInfoField*pRspInfo, int nRequestID, bool bIsLast) {};

// 登录请求响应    
typedef void OnRspUserLoginType(CThostFtdcRspUserLoginField *pRspUserLogin, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast);
void *_OnRspUserLogin;
virtual void OnRspUserLogin(CThostFtdcRspUserLoginField *pRspUserLogin, CThostFtdcRspInfoField *pRspInfo, int nRequestID, bool bIsLast){
  if (_OnRspUserLogin) {
    ((OnRspUserLoginType*)_OnRspUserLogin)(pRspUserLogin, pRspInfo, nRequestID, bIsLast);
  }
}
// 登录请求响应
DLL_EXPORT void SetOnRspUserLogin(Quote *spi, void *onFunc){
    spi->_OnRspUserLogin = onFunc;
}
正则
  • 函数

    ///(.)n[v]*virtuals+(w+)s+(w+)(([)]))

  • 参数

    (w+)s+([*])?s?(w+)

模板

(在原 quote.h quote.cpp修改)

  • quote.h.tpl

# include "../CTPv6.6.8_20220712/ThostFtdcMdApi.h"

class Quote: CThostFtdcMdSpi{
public:
 [[ range .On ]]// [[ .Comment ]]
    typedef void [[ .Name ]]Type([[ range $idx, $param := .Params ]][[ if gt $idx 0 ]], [[ end ]][[ .Type ]] [[ if .HasStar ]]*[[ end ]][[ .Var ]][[ end ]]);
void*_[[ .Name ]];
    virtual void [[ .Name ]]([[ range $idx, $param := .Params ]][[ if gt $idx 0 ]], [[ end ]][[ .Type ]] [[ if .HasStar ]]*[[ end ]][[ .Var ]][[ end ]]){
        if (_[[ .Name ]]) {
   (([[ .Name ]]Type*)_[[ .Name ]])([[ range $idx, $param := .Params ]][[ if gt $idx 0 ]], [[ end ]][[ .Var ]][[ end ]]);
  }
    }
 [[ end ]]
};

  • quote.cpp.tpl
#define DLL_EXPORT extern "C"

#include "quote.h"
#include <iostream>

using namespace std;

DLL_EXPORT void* CreateFtdcMdApi(const char *pszFlowPath = "", const bool bIsUsingUdp=false, const bool bIsMulticast=false){
    cout << pszFlowPath << endl;
    return CThostFtdcMdApi::CreateFtdcMdApi(pszFlowPath, bIsUsingUdp, bIsMulticast);
}

// 创建 Quote 实例
DLL_EXPORT void* CreateFtdcMdSpi() {
    return new Quote(); 
}

// ******** 调用函数 *********
[[ range .Fn]]// [[ .Comment ]]
DLL_EXPORT [[ .RtnType ]] [[ .Name ]](CThostFtdcMdApi *api[[ range .Params ]], [[ .Type ]] [[ if .HasStar ]]*[[ end ]][[ .Var ]][[ if eq .Var "ppInstrumentID" ]][][[ end ]][[ end ]]){
    cout << "[[ .Name ]]" << endl;
    return api->[[ .Name ]]([[ range $idx, $param := .Params ]][[ if gt $idx 0 ]], [[ end ]][[ .Var ]][[ end ]]);
}
[[ end ]]

// **** 用 Set 函数将 go 函数指针赋值给 C 函数指针 ****
[[ range .On ]]// [[ .Comment ]]
DLL_EXPORT void Set[[ .Name ]](Quote *spi, void *onFunc){
    spi->_[[ .Name ]] = onFunc;
}
[[ end ]]
  • 特别修正(pInstrument[])

    [[ if eq .Var “ppInstrumentID” ]][][[ end ]]

quote.go

在原 main.go 基础上修改

类型转换
  • c → cgo

    c 类型 cgo 类型 转换
    struct struct CThostFtdcFensUserInfoField
    → struct CThostFtdcFensUserInfoField
    CThostFtdcMdSpi * void *
  • c -> go

    c go 转换
    CThostFtdcMdSpi * unsafe.Pointer
    struct * *struct CThostFtdcFensUserInfoField
    →*def.CThostFtdcFensUserInfoField
    char* string
    char *[] []string
  • go → cgo 调用函数

    go cgo 转换
    *struct *C.struct_ (*C.struct_%s)(unsafe.Pointer(%s))”, typ, name
    string *C.char “C.CString(%s)”, name
    int C.int “C.int(%s)”, name
    • [ ]string → char *ppInstrumentID[]

// 订阅询价。
func (q *Quote)SubscribeForQuoteRsp(ppInstrumentID []string, nCount int){
    instruments := make([]*C.char, len(ppInstrumentID))
 for i := 0; i < len(instruments); i++ {
  instruments[i] = (*C.char)(unsafe.Pointer(C.CString(ppInstrumentID[i])))
 }
 C.SubscribeForQuoteRsp(q.api, (**C.char)(unsafe.Pointer(&amp;instruments[0])), C.int(len(instruments)))
}

  • cgo → go 响应函数

    cgo go 转换
    *C.struct_ *struct “(*def.%s)(unsafe.Pointer(%s))”, typ, name
    *C.char string “C.GString(%s)”, name
    C.int int “int(%s)”, name
    C._Bool bool “bool(%s)”, name
模板
package quote

/*
#cgo CPPFLAGS: -fPIC -I../../CTPv6.6.8_20220712
#include "ThostFtdcUserApiDataType.h"
#include "ThostFtdcUserApiStruct.h"

#cgo linux LDFLAGS: -fPIC -L${SRCDIR}/../lib -Wl,-rpath ${SRCDIR}/../lib -l ctpquote -lstdc++

void* CreateFtdcMdApi(char const*, _Bool, _Bool);
void* CreateFtdcMdSpi();

[[ range .Fn ]]// [[ .Comment ]]
[[ .RtnType ]] [[ .Name ]](void *api[[ range .Params ]], [[ .Type|toCGo ]] [[ if .HasStar ]]*[[ end ]][[ .Var ]][[ if eq .Var "ppInstrumentID" ]][][[ end ]][[ end ]]);
[[ end ]]
[[ range .On ]]// [[ .Comment ]]
void Set[[ .Name ]](void *, void *);
void ex[[ .Name ]]([[ range $idx, $p := .Params ]][[ if gt $idx 0 ]], [[ end]][[ .Type|toCGo ]] [[ if .HasStar ]]*[[ end ]][[ .Var ]][[ if eq .Var "ppInstrumentID" ]][][[ end ]][[ end ]]);
[[ end ]]

#include <stdlib.h>
#include <stdint.h>
*/
import "C"

import (
 "fmt"
 "goctp/def"
 "os"
 "time"
 "unsafe"
)

type Quote struct {
 api, spi unsafe.Pointer
 // ************ 响应函数变量 ******************
 [[ range .On -]]
 // [[ .Comment ]]
 [[ .Name ]] func([[ range $idx, $p := .Params ]][[ if gt $idx 0 ]], [[ end ]][[ .Var ]] [[ toGoType .Type .Var ]][[ end ]])
 [[ end -]]
}

var q *Quote

func NewQuote() *Quote {
    if q != nil{
        return q
    }
    q = &amp;Quote{}
 path := C.CString("./log/")
 os.MkdirAll("./log/", os.ModePerm)

 q.api = C.CreateFtdcMdApi(path, false, false)

 q.spi  = C.CreateFtdcMdSpi()
 C.RegisterSpi(q.api, q.spi)

    [[ range .On -]]
 // [[ .Comment ]]
 C.Set[[ .Name ]](q.spi, C.ex[[ .Name ]])
    [[- end ]]
    return q
}

[[ range .On -]]
// [[ .Comment ]]
//
//export ex[[ .Name ]]
func ex[[ .Name ]]([[ range $idx, $p := .Params ]][[ if gt $idx 0 ]], [[ end ]][[ .Var ]] [[ if .HasStar ]]*[[ end ]][[ .Type|exToCGo ]][[ end ]]) {
 if q.[[ .Name ]] == nil {
  fmt.Println("[[ .Name ]]")
 } else {
  q.[[ .Name ]]([[ range $idx, $p := .Params ]][[ if gt $idx 0 ]], [[ end ]][[ onVar .Type .Var ]][[ end ]])
 }
}
[[ end ]]

[[ range .Fn ]]// [[ .Comment ]]
func (q *Quote)[[ .Name ]]([[ range $idx, $p := .Params ]][[ if gt $idx 0 ]], [[ end ]][[ .Var ]] [[ toGoType .Type .Var ]][[ end ]]){
    [[ if and (gt (len .Params) 0) (eq (index .Params 0).Var "ppInstrumentID") -]]
 instruments := make([]*C.char, len(ppInstrumentID))
 for i := 0; i < len(instruments); i++ {
  instruments[i] = (*C.char)(unsafe.Pointer(C.CString(ppInstrumentID[i])))
 }
 C.[[ .Name ]](q.api, (**C.char)(unsafe.Pointer(&amp;instruments[0])), C.int(len(instruments)))
 [[- else -]]
 C.[[ .Name ]](q.api[[ range .Params ]], [[ fnVar .Type .Var ]][[ end ]])
 [[- end ]]
}
[[ end ]]

trade.h trade.cpp

以 quote.h.tpl quote.cpp.tpl 为基础进行修改

trade.h.tpl
  • ThostFtdcMdApiThostFtdcTraderApi

  • QuoteTrade

  • CThostFtdcMdSpiCThostFtdcTraderSpi

trade.cpp.tpl

fn函数前加t, set函数前加t, 以避免与 quote 重名

  • quote.htrade.h

  • CreateFtdcMdApi(const char *pszFlowPath = "", const bool bIsUsingUdp=false, const bool bIsMulticast=false)CreateFtdcTraderApi(const char *pszFlowPath = "")

  • CThostFtdcMdApi::CreateFtdcMdApiCThostFtdcTraderApi::CreateFtdcTraderApi

  • CThostFtdcMdApiCThostFtdcTraderApi

  • QuoteTrade

// 创建 Quote 实例
DLL_EXPORT void* CreateFtdcMdSpi() {
    return new Quote(); 
}
DLL_EXPORT void* GetVersion(){
    return (void*)CThostFtdcTraderApi::GetApiVersion();
}

// 创建 Trade 实例
DLL_EXPORT void* CreateFtdcTraderSpi() {
    return new Trade(); 
}

trade.go

fn函数前加t, set函数前加t, 以避免与 quote 重名

模板
package trade

/*
#cgo CPPFLAGS: -fPIC -I../../CTPv6.6.8_20220712
#include "ThostFtdcUserApiDataType.h"
#include "ThostFtdcUserApiStruct.h"

#cgo linux LDFLAGS: -fPIC -L${SRCDIR}/../lib -Wl,-rpath ${SRCDIR}/../lib -l ctptrade -lstdc++

void* CreateFtdcTraderApi(char const*);
void* CreateFtdcTraderSpi();
void* GetVersion();

[[ range .Fn ]]// [[ .Comment ]]
[[ .RtnType ]] t[[ .Name ]](void *api[[ range .Params ]], [[ .Type|toCGo ]] [[ if .HasStar ]]*[[ end ]][[ .Var ]][[ if eq .Var "ppInstrumentID" ]][][[ end ]][[ end ]]);
[[ end ]]
[[ range .On ]]// [[ .Comment ]]
void tSet[[ .Name ]](void *, void *);
void [[ .Name ]]([[ range $idx, $p := .Params ]][[ if gt $idx 0 ]], [[ end]][[ .Type|toCGo ]] [[ if .HasStar ]]*[[ end ]][[ .Var ]][[ if eq .Var "ppInstrumentID" ]][][[ end ]][[ end ]]);
[[ end ]]

#include <stdlib.h>
#include <stdint.h>
*/
import "C"

import (
 "fmt"
 "goctp/def"
 "os"
 "unsafe"
)

type Trade struct {
 api, spi unsafe.Pointer
 Version string

 // ************ 响应函数变量 ******************
 [[ range .On -]]
 // [[ .Comment ]]
 [[ .Name ]] func([[ range $idx, $p := .Params ]][[ if gt $idx 0 ]], [[ end ]][[ .Var ]] [[ toGoType .Type .Var ]][[ end ]])
 [[- end]]
}

var t *Trade

func NewTrade() *Trade {
    if t != nil{
        return t
    }
    t = &Trade{}
 path := C.CString("./log/")
 os.MkdirAll("./log/", os.ModePerm)

 t.api = C.CreateFtdcTraderApi(path)
 t.Version = C.GoString((*C.char)(C.GetVersion()))
 fmt.Println(t.Version)

 t.spi  = C.CreateFtdcTraderSpi()
 C.tRegisterSpi(t.api, t.spi)

    [[ range .On -]] 
 C.tSet[[ .Name ]](t.spi, C.[[ .Name ]]) // [[ .Comment ]]
    [[ end ]]
    return t
}

[[ range .On -]]
//export [[ .Name ]]
func [[ .Name ]]([[ range $idx, $p := .Params ]][[ if gt $idx 0 ]], [[ end ]][[ .Var ]] [[ if .HasStar ]]*[[ end ]][[ .Type|exToCGo ]][[ end ]]) {
 if t.[[ .Name ]] == nil {
  fmt.Println("[[ .Name ]]")
 } else {
  t.[[ .Name ]]([[ range $idx, $p := .Params ]][[ if gt $idx 0 ]], [[ end ]][[ onVar .Type .Var ]][[ end ]])
 }
}
[[ end ]]

[[ range .Fn ]]// [[ .Comment ]]
func (t *Trade)[[ .Name ]]([[ range $idx, $p := .Params ]][[ if gt $idx 0 ]], [[ end ]][[ .Var ]] [[ toGoType .Type .Var ]][[ end ]]){
 C.t[[ .Name ]](t.api[[ range .Params ]], [[ fnVar .Type .Var ]][[ end ]])
}
[[ end ]]

byte to Json

// 交易用户登录统计类型类型
type TShfeFtdcLoginStatTypeType byte
// JSON 转换
func (t TShfeFtdcLoginStatTypeType) MarshalJSON() ([]byte, error) {
 return json.Marshal(strings.Trim(string(t), "x00"))
}

// 按同一用户统计
const SHFE_FTDC_RLST_ByUser TShfeFtdcLoginStatTypeType = '0'

// 按同一IPMAC地址统计
const SHFE_FTDC_RLST_ByAddress TShfeFtdcLoginStatTypeType = '1'

[…]byte 转 string 再转 json

// 转换函数,注意去掉尾部的 x00
func getGBK(bs []byte) string {
    str, _ := simplifiedchinese.GBK.NewDecoder().String(strings.Trim(string(bs), "x00"))
    return str
}

// 交易所交易员代码类型
type TShfeFtdcTraderIDType [21]byte
// JSON 转换
func (t TShfeFtdcTraderIDType) MarshalJSON() ([]byte, error) { return json.Marshal(getGBK(t[:])) }

原文地址:https://blog.csdn.net/along1976/article/details/129108041

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_24046.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注