4

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.

1 Answer 1

1

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);
}

Sign up to request clarification or add additional context in comments.

1 Comment

I got a very similar answer from Google's AI. In particular, I think it hallucinated the scalar_type keyword. If you look at the documentation for CppConfig (symforce.org/api/symforce.codegen.backends.cpp.cpp_config.html), there's no mention of this argument, nor is it in source. Am I missing something? I'm also not seeing a namespace keyword in CppConfig.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.