Now I'm working on GO RPC, I'm using gRPC+Protobuf. And I follow openconfig's data struct, so I could NOT redesign.
I need to fill protobuf struct and Marshal it and send it out, and then client will Unmarshal it and read datas.
My protobuf file(xxx.pb.go) is complicated, for example, just like this:
type ObjBase struct {
a *ObjChildAlice,
b *ObjChildBob,
... // there are many variables like ObjChildCheer, ObjChildDog ...
}
type ObjChildAlice struct {
child *ObjChildChild,
}
type ObjChildBob struct {
child *ObjChildChild,
}
// there are many types like ObjChildCheer, ObjChildDog ...
type ObjChildChild {
int i,
}
In server side, I need to fill ObjBase and send it out, this is my task:
// Code 1
func () {
init ob with ObjBase
if DataBase has Alice {
fill ob.a with ObjChildAlice
}
if DataBase has Bob {
fill ob.a with ObjChildBob
}
// there are many if..else.. DataBase has Cheer...
return ob
}
So I code like this first:
// Code 2
func () {
ob := new(ObjBase)
oba := new(ObjChildAlice)
obb := new(ObjChildBob)
if DataBase has Alice {
ob.a = oba
}
if DataBase has Bob {
ob.b = obb
}
...
return ob
}
But this code could NOT work, as I check member of ob.a and ob.b are all zero.
So I change like this:
// Code 3
func () {
if DataBase has Alice && DataBase has Bob {
ob := &ObjBase{
a: &ObjChildAlice{},
b: &ObjChildBob{},
}
} else if DataBase has Alice && NOT DataBase has Bob {
ob := &ObjBase{
a: &ObjChildAlice{},
}
} else if ...
return ob
}
This works. But in database, there are kinds of variables like Alice, Bob, Cheer, Dog ... It's impossible to use if..else.. to do this work.
So I have questions:
Why ob'member in Code2 is zero?
is there any way to set Go struct's member object dynamically?
Please take a look at this doc which talks the Go generated code for protobufs. https://developers.google.com/protocol-buffers/docs/reference/go-generated
You should be able to set a protobuf message field in the generated code by directly accessing the corresponding member field (which is exported).
Related
Let's say I have two different structs:
type One struct {
Id string
// Other fields
}
type Two struct {
Id string
// Other fields
}
Is it possible to define a function that accepts both One and Two without explicitly listing them as options?
E.g. I am looking for something like this:
type ModelWithId struct {
Id string
}
func Test[M ModelWithId](m M) {
fmt.PrintLn(m.Id)
}
one := One { Id: "1" }
Test(one) // Prints 1
I don't want to use funcTest[M One | Two](m M), because I'll likely have 10+ structs and I don't want to come back to the function every time I add a new struct to the codebase.
Generics constraints the type parameter behaviours using methods, so you need to rewrite your code as:
type One struct {
id string
}
func (o *One) Id() string {
return o.id
}
then your use site would become:
type ModelWithId interface {
Id() string
}
func Test[M ModelWithId](m M) {
fmt.Println(m.Id())
}
There're 2 entities in a Go program: country states and years. A program calculates some data by receiving input from a user.
The list of country states is constant, whereas years, as the time goes on, of course not.
I want to structure my program in such a way that it'll be flexible from the point of view of adding new year without changing the existing code.
How would I do it? I need an architecture pattern for such a case, that is.
I want to avoid something like this:
func CalculateData(cs, year) -> ReturnDataStructure {
if year == 2000 {
return calculateDataFor2000(cs)
} else if year == 2001 {
return calculateDataFor2001(cs)
} else if year == 2002 {
return calculateDataFor2002(cs)
} else //and so on
// as years go by, add a new year...
} else if year == 2052 {
return calculateDataFor2052(cs)
}
//....
}
Instead I want to be able to add only: I'd add an implementation, perhaps in a separate file, without having to touch the existing code, as the years go by.
That is, the function CalculateData(...) should become this flexible, or extensible, for it work properly and it shouldn't know how many years there are. The years must not be hard-coded.
Each new implementation must be compiled statically.
How would one do it?
If you want to write Go code to do the calculations, it seems you're stuck with compiling it in. There's no Go equivalent to the opening of a shared object file. See Load package dynamically.
If I understand your question, your problem is that you don't know exactly what math will need to be performed for each year, so you don't want to hard code the math into your application. You don't need a dynamic library to do that. You can either build your own expression evaluator (see "The Go Programming Language" 7.9), or use an embeddable language such as Lua (https://pkg.go.dev/github.com/Shopify/go-lua). Either way, you can move the math "out" of the Go code and associate it with its year in a file. Each year you can write the math for that year, and then the Go won't have to change.
I suggest to use a package level variable of type map[string]func(...) so that for each year you can set an handler.
If you need to organize those functions into separate packages, you can consider adding a public Register function and let them register the handler at initialization during runtime execution.
package main
import (
"fmt"
)
func main() {
fmt.Println("Hello, playground")
}
var computeYears = map[string]func(cs string) interface{}{
"2000": calculateDataFor2000,
"2001": calculateDataFor2001,
"2002": func(cs string) interface{} { return nil },
}
func CalculateData(cs, year string) interface{} {
x, ok := computeYears["year"]
if ok {
return x(cs)
}
/*
if year == 2000 {
return calculateDataFor2000(cs)
} else if year == 2001 {
return calculateDataFor2001(cs)
} else if year == 2002 {
return calculateDataFor2002(cs)
} else //and so on
// as years go by, add a new year...
} else if year == 2052 {
return calculateDataFor2052(cs)
}
*/
panic("no such implmentation for the year " + year)
return nil
}
func calculateDataFor2000(cs string) interface{} {
return nil
}
func calculateDataFor2001(cs string) interface{} {
return nil
}
// In the case you need to delcare thse function in separate packages,
// use a public function t let them regser at init time.
func Register(year string, handler func(cs string) interface{}) {
computeYears[year] = handler
}
func init() {
fmt.Println("init time")
Register("2003", calculateDataFor2000)
}
https://play.golang.org/p/zI8GCCPj1ew
I am trying to solve a problem with code duplication to make it less error-prone.
I'm working with different Data Sources each with some specific properties. Here is how my data model looks like:
mod sources {
pub struct Ds1;
pub struct Ds2;
//hundreds more
pub trait DsTypeTrait{
type Input;
type Output;
}
impl DsTypeTrait for Ds1 {
type Input = u32;
type Output = u32;
}
impl DsTypeTrait for Ds2 {
type Input = String;
type Output = String;
}
//etc...
enum DataSource{
Ds1(Ds1),
Ds2(Ds2),
//...
}
}
So any time somebody wants to add support for a new data source they need to add it to the enum DataSource. The PROBLEM with the solution is that if another module contains some custom data format for, e.g. DataSource communications it would be required to add the DataSource in the 2 places which is extremely error-prone. For example:
mod some_other {
use super::sources::*;
struct DataSourceDataInputOutput<Ds: DsTypeTrait>{
input: <Ds as DsTypeTrait>::Input,
output: <Ds as DsTypeTrait>::Output
}
enum DataSourceIO{
Ds1(DataSourceDataInputOutput<Ds1>),
Ds2(DataSourceDataInputOutput<Ds2>)
//Extremely easy to forget to add here
}
}
QUESTION: Given the enum DataSource is it possible to write a macro to generate enum DataSourceIO automatically and avoid modifying enum DataSourceIO manually each time a new DataSource is added?
It seems enum parsing is a complicated issue so it is reasonable to move the DataSource actual declaration in the macro itself. Here is what I came up with:
#[macro_use]
mod sources {
macro_rules! data_sources {
($name:ident, $type:tt) => {
enum $name{
Ds1($type<Ds1>),
Ds2($type<Ds2>)
}
}
}
//...
}
mod some_other {
struct DataSourceDataInputOutput<Ds: DsTypeTrait>{
input: <Ds as DsTypeTrait>::Input,
output: <Ds as DsTypeTrait>::Output
}
data_sources!(Test, DataSourceDataInputOutput);
fn test_fn() {
let ds1io: DataSourceDataInputOutput<Ds1> = DataSourceDataInputOutput{
input: 1,
output: 2
};
let test = DataSourceIO::Ds1(ds1io); //works ok
}
}
I have a meal struct that "appends" another struct, except I want to add another struct "mealComponents".
type mealMain struct {
*model.Meal
Components []mealComponent `json:"components"`
}
type mealComponent struct {
*model.MealComponent
}
Where *model.Meal is as follows
type Meal struct {
ID int64 `json:"id"`
}
What I want is basically for "mealMain" struct to act like "Meal" struct, so that I can assign values and somehow append mealComponent as child (or maybe this is not a good idea? I'm open to suggestions)
However when I do something like this
var meal mealMain
meal.ID = 1
It throws runtime error: invalid memory address or nil pointer dereference at meal.ID assignment.
But if I do it like this:
type mealMain struct {
MealMain *model.Meal `json:"meal_main"`
Components []mealComponent `json:"components"`
}
Then assign it this way:
var meal mealMain
meal.mealMain.ID = 1
It works properly, but I have the return json even deeper like this:
{
"MealModel": {
"id": 1
}
}
What I want is this:
{
"id": 1
}
Note: I want to avoid changing the model.
If you don't want to change the model:
var meal = mealMain{
Meal: &Meal{},
}
meal.ID = 1
The point is that in the following struct *Meal is set to nil if you don't initialize it.
type mealMain struct {
*Meal
Components []mealComponent `json:"components"`
}
I'd probably create a function to never have to worry about the correct initialization ever again:
func newMealMain() mealMain {
return mealMain{
Meal: &Meal{},
}
}
Then your code would be:
var meal = newMealMain()
meal.ID = 1
I have a json file (nested json) that I am unmarshalling its content into a map[string]interface. Now I have to implement pagination as the data is large. The client side will send as a query parameter the desired page, how can I slice the data I have?
This is a snippet of the data I am dealing with:
"packages":{
"pkg1": {
"meta": {
"description": "description1",
"name": "pkg1.1"
},
"name": "pkg1.1"
},
"pkg2": {
"meta": {
"description": "description2",
"name": "pkg2.2"
},
"name": "pkg2.2"
},
}
So what I did is that I recursively iterated through the data and created an array of a custom type containing the data I need (name, description) for each entry so that I can use it for pagination. Here is the code I used:
type Object struct {
name string
description string
}
func iterate(aMap map[string]interface{}, result *[]Object){
for key, val := range aMap {
switch val.(type) {
case map[string]interface{}:
if(key == "meta"){
switch reflect.TypeOf(val).Kind() {
case reflect.Map:
s := reflect.ValueOf(val)
var tmpData Object
if(s.MapIndex(reflect.ValueOf("name")).IsValid()){
tmpData.name = s.MapIndex(reflect.ValueOf("name")).Interface().(string)
}
if(s.MapIndex(reflect.ValueOf("description")).IsValid()){
tmpData.description = s.MapIndex(reflect.ValueOf("description")).Interface().(string)
}
*result = append(*result, tmpData)
}
}
iterate(val.(map[string]interface{}), result)
default: //DO NOTHING!!
}
}
}
If you're doing pagination, somewhere the data must be represented as a list instead of an object? I assume at some place in your JSON, you have a list of items, otherwise pagination doesn't make sense.
It shouldn't be very hard, something simple like this should work:
const (
itemsPerPage = 10
)
var data []map[string]interface{}
// pages start at 1, can't be 0 or less.
func GetDataPage(page int) []map[string]interface{} {
start := (page - 1) * itemsPerPage
stop := start + itemsPerPage
if start > len(data) {
return nil
}
if stop > len(data) {
stop = len(data)
}
return data[start:stop]
}
You are unmarshalling your json into a map which has no order by itself. In order to be able to paginate your results you need to order them in some way.
One way of doing it is to sort your data and then store it into an array. But in order to paginate you need to have ordered data and that is not possible with a map.