AddOperation adds an operation to g.
(args OpSpec)
| 278 | |
| 279 | // AddOperation adds an operation to g. |
| 280 | func (g *Graph) AddOperation(args OpSpec) (*Operation, error) { |
| 281 | if args.Name == "" { |
| 282 | args.Name = args.Type |
| 283 | } |
| 284 | cname := C.CString(args.Name) |
| 285 | ctype := C.CString(args.Type) |
| 286 | cdesc := C.TF_NewOperation(g.c, ctype, cname) |
| 287 | C.free(unsafe.Pointer(cname)) |
| 288 | C.free(unsafe.Pointer(ctype)) |
| 289 | |
| 290 | for _, in := range args.Input { |
| 291 | switch in := in.(type) { |
| 292 | case Output: |
| 293 | C.TF_AddInput(cdesc, in.c()) |
| 294 | case OutputList: |
| 295 | size := len(in) |
| 296 | list := make([]C.TF_Output, size) |
| 297 | for i, v := range in { |
| 298 | list[i] = v.c() |
| 299 | } |
| 300 | if size > 0 { |
| 301 | C.TF_AddInputList(cdesc, &list[0], C.int(size)) |
| 302 | } else { |
| 303 | C.TF_AddInputList(cdesc, nil, 0) |
| 304 | } |
| 305 | } |
| 306 | } |
| 307 | for _, in := range args.ControlDependencies { |
| 308 | C.TF_AddControlInput(cdesc, in.c) |
| 309 | } |
| 310 | status := newStatus() |
| 311 | for name, value := range args.Attrs { |
| 312 | if err := setAttr(cdesc, status, name, value); err != nil { |
| 313 | // Memory leak here as the TF_OperationDescription |
| 314 | // object will not be cleaned up. At the time of this |
| 315 | // writing, this was next to impossible since it |
| 316 | // required value to be a string tensor with |
| 317 | // incorrectly encoded strings. Given this rarity, live |
| 318 | // with the memory leak. If it becomes a real problem, |
| 319 | // consider adding a TF_DeleteOperationDescription |
| 320 | // function to the C API. |
| 321 | return nil, fmt.Errorf("%v (memory will be leaked)", err) |
| 322 | } |
| 323 | } |
| 324 | if len(args.Device) > 0 { |
| 325 | cdevice := C.CString(args.Device) |
| 326 | C.TF_SetDevice(cdesc, cdevice) |
| 327 | C.free(unsafe.Pointer(cdevice)) |
| 328 | } |
| 329 | c := C.TF_FinishOperation(cdesc, status.c) |
| 330 | if err := status.Err(); err != nil { |
| 331 | return nil, err |
| 332 | } |
| 333 | return &Operation{c, g}, nil |
| 334 | } |
| 335 | |
| 336 | func setAttr(cdesc *C.TF_OperationDescription, status *status, name string, value interface{}) error { |
| 337 | cAttrName := C.CString(name) |