Как перепечатать функцию в Голанге? - PullRequest
0 голосов
/ 02 мая 2018

В моем текущем проекте Golang я использую буферизацию журналов перед отправкой их в Elasticsearch в моей библиотеке журналов. Я хочу представить что-то вроде функции C atexit() для очистки всех ожидающих журналов в случае неожиданного выхода.

Я нашел библиотеку atexit , но в моем случае ее было недостаточно, поскольку она не позволяет передавать аргументы в функции-обработчики. Я решил написать свою версию и в итоге получил примерно аналогичную структуру. У меня есть модуль atexit:

package atexit

import (
    "fmt"
    "os"
    "reflect"
)

var handlers []interface{}
var params []interface{}

func runHandler(handler interface{}, p interface{}) {
    defer func() {
        if err := recover(); err != nil {
            fmt.Fprintln(os.Stderr, "error: atexit handler error:", err)
        }
    }()

    f := reflect.ValueOf(handler)
    s := reflectSlice(p)
    fmt.Printf("%#v", s)
    f.Call(s)
}

func reflectSlice(slice interface{}) []reflect.Value {
    s := reflect.ValueOf(slice)
    if s.Kind() != reflect.Slice {
        panic("InterfaceSlice() given a non-slice type")
    }

    ret := make([]reflect.Value, s.Len())

    for i:=0; i<s.Len(); i++ {
        ret[i] = reflect.ValueOf(s.Index(i))
    }

    return ret
}

func runHandlers() {
    for i, handler := range handlers {
        runHandler(handler, params[i])
    }
}

func Exit(code int) {
    runHandlers()
    os.Exit(code)
}

func Register(handler interface{}, p interface{}) {
    f := reflect.TypeOf(handler)
    if f.Kind() != reflect.Func {
        panic("Register() given a non-function type")
    }
    handlers = append(handlers, handler)
    params = append(params, p)
}

и я звоню из основной программы:

package main
import (
    "fmt"
    "./atexit"
    "encoding/json"
    "reflect"
)

type Batch struct {
    Index string        `json:"index"`
    Type string     `json:"_Type"`
    Content interface{} `json:"Content"`
}

func flush(b ...interface{}) {
    for _, entry := range(b) {
        fmt.Printf("%#v\n", entry)
        fmt.Println(reflect.TypeOf(reflect.ValueOf(entry)))
        fmt.Println(reflect.TypeOf(entry))
        a, err  := json.Marshal(entry)
        if err != nil {
            fmt.Println("error!")
        }
        fmt.Println(string(a))
    }
}

func handler(v ...interface{}) {
    fmt.Println("Exiting")
    for _, batch := range(v) {
        fmt.Printf("%#v\n", batch)
    }
}

func main() {
    type ColorGroup struct {
        ID     int
        Name   string
        Colors []string
    }
    group := ColorGroup{
        ID:     1,
        Name:   "Reds",
        Colors: []string{"Crimson", "Red", "Ruby", "Maroon"},
    }
    fmt.Printf("%#v\n", group)
    r, err := json.Marshal(group)
    if err != nil {
        fmt.Println("error:", err)
    }
    fmt.Println(string(r))


    b := []Batch{Batch{"index", "type", "content1"},Batch{"index", "type", "content2"}}
    atexit.Register(handler, b)
    atexit.Register(flush, []ColorGroup{group})
    atexit.Exit(0)
}

Как видите, вызывая reflect.ValueOf(), я получаю структуру reflect.Value, которая затем передается функции обратного вызова. Кажется, проблема в том, что эта структура не содержит метаданных об экспорте json или неправильно обрабатывается с помощью json.Marshal(), который затем выводит пустой json. Есть ли способ, которым я могу передать правильную структуру []interface{} в функцию обратного вызова или какой-то подобный механизм, который будет делать примерно то, что я пытаюсь достичь? Обратите внимание, что я хочу общий механизм обратного вызова, который не должен зависеть от типа, передаваемого ему. Пока что это кажется невозможным или, по крайней мере, очень ограниченным f.Call(s), который вызывается в runHandler() и в качестве параметра принимается отражение. Значение.

1 Ответ

0 голосов
/ 02 мая 2018

Вы не должны использовать отражение для этого. Вы должны сделать ваши обработчики типа func(), а затем просто передать анонимную функцию (возможно, замыкание), которая обрабатывает любые необходимые аргументы напрямую. Пример:

atexit.Register(func() {
    handler(b)
})
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...