Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to call a SymForce auto-generated function with a user defined type?

Suppose I have a Python function Foo() that I'm auto-generating to C++ with SymForce's codegen functionality. It's a complicated function with a lot of parameters, so I want to pass in a Params struct instead of 20+ individual scalar arguments. I would further like to use the existing Params struct in use by the rest of the codebase instead of copying data to the auto-generated SymForce struct.

The Code generation using implicit functions in the Codegen Tutorial seems to be fairly close to what I want for part (1) of this question, but if you look at the generated types in double_pendulum.lcm, everything is using double which could be a dealbreaker if I can't replace the type. Speaking of which, it's not clear how to change out this type for my user defined Params struct.

Playing around a bit, I'm able to get similar results with toy problems such as:

@dataclasses.dataclass
class Params:
    a: np.float32  # Single precision is not respected.
    b: np.float32

def foo(params: Params):
    return params.a

...which auto-generates the following:

struct params_t {
  double a;
  double b;
}

// ...

template <typename Scalar>
Scalar Foo(const foo::params_t& params) {
  // ...
  Scalar _res;

  _res = params.a;

  return _res;
}  // NOLINT(readability/fn_size)

Again, there's not an obvious way to replace params_t and, giving up on wholesale type replacement, there's not an obvious way to change the type of fields from double to float.

The Generating from a Python function example is able to map the sf.Pose3 type to the templated C++ type sym::Pose3<Scalar>, so there's clearly a way to do this, but it appears to involve deeper spelunking into the source code.

like image 938
user2465116 Avatar asked Dec 01 '25 10:12

user2465116


1 Answers

SymForce can change the scalar in the generated struct, but it cannot swap the whole struct for one you already have.

The generator should use a templated scalar instead of hard‑wiring double:

from symforce.codegen.backends.cpp import CppConfig
from symforce import codegen as cg

cfg = CppConfig(
    namespace="foo",
    scalar_type="T",          # make everything Scalar
)

cg.Codegen.function(foo, config=cfg, name="Foo").generate_function("/tmp/out")

With scalar_type="T" every member of params_t becomes Scalar, so in C++ you can do:

foo::params_t p;
p.a = 1.0f;           
auto out = foo::Foo<float>(p);

Write a tiny wrapper if you really must pass your existing Params instead of

foo::params_t:

inline float Foo(const MyPkg::Params& in) {
  foo::params_t tmp;
  tmp.a = static_cast<float>(in.a);
  tmp.b = static_cast<float>(in.b);
  // … copy the rest …
  return foo::Foo<float>(tmp);
}

like image 90
Silikazi Avatar answered Dec 03 '25 00:12

Silikazi



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!