接口封装——混合编程 c+ c go

4 篇文章 0 订阅
3 篇文章 0 订阅

定义

  • 函数

    • 调用函数

    • 响应函数

  • 方法

    • 类的函数

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

过程

  • 调用函数

    go ➡️cgo➡️c➡️c++

  • 响应函数

    c++➡️c➡️cgo➡️go

类型对应

C语言类型CGO类型Go语言类型
charC.charbyte
singed charC.scharint8
unsigned charC.ucharuint8
shortC.shortint16
unsigned shortC.ushortuint16
intC.intint32
unsigned intC.uint / unsigned intuint32
longC.longint32
unsigned longC.ulonguint32
long long intC.longlongint64
unsigned long long intC.ulonglonguint64
floatC.floatfloat32
doubleC.doublefloat64
size_tC.size_tuint
boolC._Boolbool
struct xxC.struct_xxtype xx struct
char **C.charstring
void **C.voidunsafe.Pointer

准备

  • 下载官方接口

  • 文件编码 GB2312 → Utf8

  • 回车 CRLF → LF

  • 目录结构

|- 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 
  • go

    libctpquote.so 需要 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(&f)), nRequestID)
...
}

  • go → Cgo

    f CThostFtdcReqUserLoginField → (*C.struct_CThostFtdcReqUserLoginField)(unsafe.Pointer(&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]*(///[^;]+)?typedef\s+(\w+)\s+(\w+)(?:[(\d+)])? → // $1\ntype $4 [$5]byte

    • ///\s*(.*)\n#define\s+(\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*)\sstruct\s+(\w+)\s{([^}]*)} → // $1\ntype $2 struct {}

    • ///(\S*)\s*(\w+)\s+([^;]+) → // $1\n$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]*virtual\s+(\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 类型转换
    structstructCThostFtdcFensUserInfoField
    → struct CThostFtdcFensUserInfoField
    CThostFtdcMdSpi *void *
  • c -> go

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

    gocgo转换
    *struct*C.struct_(*C.struct_%s)(unsafe.Pointer(%s))", typ, name
    string*C.char“C.CString(%s)”, name
    intC.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(&instruments[0])), C.int(len(instruments)))
}

  • cgo → go 响应函数

    cgogo转换
    *C.struct_*struct“(*def.%s)(unsafe.Pointer(%s))”, typ, name
    *C.charstring“C.GString(%s)”, name
    C.intint“int(%s)”, name
    C._Boolbool“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 = &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(&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[:])) }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

haifengat

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值