Можно ли разделить пользовательский тип данных между плагином go и приложением? - PullRequest
0 голосов
/ 06 мая 2018

Я знаю, что можно найти экспортированные символы go-plugin и ввести их в interface . Однако, мне интересно, есть ли способ напечатать их, например, в структуре. Есть ли способ сделать это?

Например:

plugin.go

package main

type Person struct {
    Name string
}

var (
    P = Person{
        Name: "Emma",
    }
)

app.go

package main

import (
    "fmt"
    "plugin"
    "os"
)

func main() {
    plug, err := plugin.Open("./plugin.so")
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    sym, err := plug.Lookup("P")
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    var p Person
    p, ok := sym.(Person)
    if !ok {
        fmt.Println("Wrong symbol type")
        os.Exit(1)
    }

    fmt.Println(p.Name) 
}

Символ P, который является Персона , обнаруживается при вызове plug.Lookup . Тем не менее, я не могу набрать P в Person , я получаю ошибку времени выполнения. В этом примере «Неверный тип символа» .

Есть ли способ добиться этого или единственный способ обмена данными между плагином и приложением с использованием интерфейсов?

Спасибо.

1 Ответ

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

Идентификаторы, определенные в пакете main, нельзя ссылать из других пакетов, поэтому экспортированный идентификатор из плагина не может быть того же типа, что и у вас в основном приложении. Даже если вы дублируете тип Person как в файле плагина, так и в вашем основном приложении, утверждение типа не удастся, потому что они не одного типа!

Но можно определить тип в отдельном пакете и использовать этот же пакет в плагине и в основном приложении. И тогда вы можете напечатать assert этот тип из символа, который вы ищете из плагина.

См. Этот пример:

Отдельный тип, определенный в его собственной упаковке:

package filter

type Filter struct {
    Name string
    Age  int
}

Код плагина:

package main

import (
    "play/filter"
)

var MyFilter = filter.Filter{
    Name: "Bob",
    Age:  21,
}

func CreateFilter() filter.Filter {
    return filter.Filter{
        Name: "Bob",
        Age:  21,
    }
}

Основное приложение:

package main

import (
    "fmt"
    "log"
    "os"
    "play/filter"
    "plugin"
)

func main() {
    p, err := plugin.Open("plugin.so")
    if err != nil {
        log.Fatal(err)
    }
    mf, err := p.Lookup("MyFilter")
    if err != nil {
        log.Fatal(err)
    }
    f, ok := mf.(*filter.Filter)
    if !ok {
        log.Fatal("Wrong symbol type")
    }

    fmt.Printf("%+v\n", f)
}

Запуск основного приложения, вывод:

&{Name:Bob Age:21}

Что вы можете заметить, так это то, что экспортируемый идентификатор в плагине MyFilter является переменной типа без указателя, но мы указали тип указателя из экспортированного символа. Это потому, что если вы посмотрите переменную , вы получите указатель на нее, иначе вы не сможете изменить значение переменной, вы можете только изменить копию. Это подробно описано в этом ответе: Символ плагина как функция возврата

Это не тот случай, если мы ищем другой символ, который экспортирует наш плагин: функция CreateFilter(), которая возвращает значение типа без указателя filter.Filter:

cf, err := p.Lookup("CreateFilter")
if err != nil {
    log.Fatal(err)
}

createFilter, ok := cf.(func() filter.Filter)
if !ok {
    log.Fatal("Wrong function type")
}
f2 := createFilter()
fmt.Printf("%+v\n", f2)

Запустив этот код, вы получите:

{Name:Bob Age:21}

См. Связанный вопрос: Плагин Go 1.8 использует пользовательский интерфейс

Также обратите внимание, что если вы измените пакет filter, обычно используемый плагином и основным приложением, вам также придется перестроить плагин. Попытка запустить приложение без перестройки плагина приведет к ошибке во время вызова plugin.Open(). Подробнее см. Как работают зависимости плагина Go?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...