Cxx: dereferencing CppPtr, accessing fields, using C++ operators

Here’s a (not so minimal) piece of code using Cxx.jl to use as an example. I include some questions in a REPL transcript below. Please let me know if I’m using Cxx not as intended.

using Cxx

cxx"""
#include <stddef.h>
#include <float.h>

typedef signed char	int8;
typedef signed short int16;
typedef signed int int32;
typedef unsigned char uint8;
typedef unsigned short uint16;
typedef unsigned int uint32;
typedef float float32;
typedef double float64;

/// A 2D column vector.
struct b2Vec2
{
	/// Default constructor does nothing (for performance).
	b2Vec2() {}

	/// Construct using coordinates.
	b2Vec2(float32 xIn, float32 yIn) : x(xIn), y(yIn) {}

	/// Set this vector to all zeros.
	void SetZero() { x = 0.0f; y = 0.0f; }

	/// Set this vector to some specified coordinates.
	void Set(float32 x_, float32 y_) { x = x_; y = y_; }

	/// Negate this vector.
	b2Vec2 operator -() const { b2Vec2 v; v.Set(-x, -y); return v; }
	
	/// Read from and indexed element.
	float32 operator () (int32 i) const
	{
		return (&x)[i];
	}

	/// Write to an indexed element.
	float32& operator () (int32 i)
	{
		return (&x)[i];
	}

	/// Add a vector to this vector.
	void operator += (const b2Vec2& v)
	{
		x += v.x; y += v.y;
	}
	
	/// Multiply this vector by a scalar.
	void operator *= (float32 a)
	{
		x *= a; y *= a;
	}


	float32 x, y;
};
"""

p = @cxxnew b2Vec2()
v = @cxx b2Vec2()

This is a REPL session that I have a few questions about


julia> typeof(v)
Cxx.CppValue{Cxx.CxxQualType{Cxx.CppBaseType{:b2Vec2},(false,false,false)},8}

julia> typeof(p)
Cxx.CppPtr{Cxx.CppValue{Cxx.CxxQualType{Cxx.CppBaseType{:b2Vec2},(false,false,false)},N},(false,false,false)}

# how to access fields of `CppValue`s?
julia> @cxx v.x
ERROR: Unrecognized CPP Expression v.x (.)

julia> @cxx p->x
0.0f0

julia> @cxx v.x = 3.0f
ERROR: Unimplemented

julia> @cxx p->x = 0
ERROR: Malformed C++ call. Expected member not x = 0

julia> @cxx (p->x) = 0
ERROR: Unimplemented

julia> @cxxnew b2Vec2(v) # copy constructor
(struct b2Vec2 *) @0x00007feca33845b0

# how to derefernece a pointer?
julia> @cxxnew b2Vec2(*p)
ERROR: syntax: "*" is not a unary operator

julia> cxx"b2Vec2 veccxx();"
true

julia> @cxx veccxx
ERROR: Cannot reference function by value

# in what universe does `veccxx` live?

# How to use C++ operators? 
julia> @cxx 2*v
error: invalid operands to binary expression ('long' and 'b2Vec2')
1 Like

The @cxx macro is convenient, but fairly limited (as sort of hinted in the readme). From limited experience, my strategy is basically to futz with icxx until I either get what I want or crash the repl. Here are some answers/suggestions that I hope will help, though if we’re lucky Keno may chime in with corrections at some point.

Assignment:

julia> icxx"$v.x = 3.0f;"
(float &) 3.0

julia> v
(struct b2Vec2) {
 .x = (float &) 3.0
 .y = (float &) 1.0e-45
}

julia> p2 = @cxx b2Vec2(icxx"*$p;")
(struct b2Vec2) {
 .x = (float &) 0.0
 .y = (float &) -1.0842022e-19
}

It lives in the Clang universe (technically the “translation unit”) as an extern definition, because there’s no function body provided. If you try running it (@cxx veccxx()), Cxx will assert out with an error about missing symbol. Here’s a simple example with a function body defined:

julia> cxx"b2Vec2 veccxx();"
true

julia> cxx"b2Vec2 veccxx() { return b2Vec2(1.0,5.42); }"
true

julia> p3 = @cxx veccxx()
(struct b2Vec2) {
 .x = (float &) 1.0
 .y = (float &) 5.42
}

julia> v = @cxx b2Vec2(1.0,5.0);

julia> icxx"""$v *= 4.0;"""

julia> v
(struct b2Vec2) {
 .x = (float &) 4.0
 .y = (float &) 20.0
}

That was extremely helpful. Thank you! In particular, I didn’t know to use the $ interpolation, and with that I think I’m in much better shape.

Just for future reference, another way to handle the error due to dereferencing, mentioned in the Cxx.jl source comments, is to get the parser to handle * differently.

julia> @cxxnew b2Vec2(*p)
ERROR: syntax: "*" is not a unary operator

julia> @cxxnew b2Vec2(*(p))
(struct b2Vec2 *) @0x00007faf17c0a770
1 Like

Just in case you haven’t seen it, the most extensive documentation is in the tests folder.

1 Like

I’m still running into some pretty basic difficulties…

using Cxx

cxx"""
#include <stddef.h>

typedef float float32;

/// A 2D column vector.
struct b2Vec2
{
	/// Default constructor does nothing (for performance).
	b2Vec2() {}

	/// Construct using coordinates.
	b2Vec2(float32 xIn, float32 yIn) : x(xIn), y(yIn) {}


	float32 x, y;
};

class b2World
{
public:
	/// Construct a world object.
	/// @param gravity the world gravity vector.
b2World(const b2Vec2& gravity);

private:
	b2Vec2 m_gravity;
};

b2World::b2World(const b2Vec2& gravity)
{
	m_gravity = gravity;
};
"""

This works

julia> icxx"""
       b2Vec2 gravity(0.0, -10.0);
       b2World world(gravity);
       world;
       """

and when I try to do that within Julia it doesn’t:


julia> gravity = icxx"""b2Vec2(-10.0, 0.0);"""
(struct b2Vec2) {
 .x = (float &) -10.0
 .y = (float &) 0.0
}


julia> world = icxx"""b2World($gravity);"""
:2:9: error: no matching constructor for initialization of 'b2World'
b2World(__juliavar1);
        ^
__cxxjl_11.cpp:29:10: note: candidate constructor not viable: requires single argument 'gravity', but no arguments were provided
b2World::b2World(const b2Vec2& gravity)
         ^
__cxxjl_11.cpp:18:7: note: candidate constructor (the implicit copy constructor) not viable: requires 1 argument, but 0 were provided
class b2World
      ^
__cxxjl_11.cpp:18:7: note: candidate constructor (the implicit move constructor) not viable: requires 1 argument, but 0 were provided
ERROR: Tried to Emit Invalid Decl
 in EmitTopLevelDecl(::Cxx.ClangCompiler, ::Cxx.CppPtr{Cxx.CxxQualType{Cxx.CppBaseType{Symbol("clang::Decl")},(false,false,false)},(false,false,false)}) at /Users/goretkin/.julia/v0.5/Cxx/src/cxxstr.jl:263
 in cxxstr_impl(...) at /Users/goretkin/.julia/v0.5/Cxx/src/cxxstr.jl:701

Gentle bump.

I think this might be a bug. You can look at the Clang AST with icxxdebug""" ... """. My hunch is that the variable is not being declared correctly, or in the correct namespace, and lookup silently fails.

The following patch might be helpful (also, history is available in Cxx.sourcebuffers array):

diff --git a/src/utils.jl b/src/utils.jl
index b34f43a..15c4c5c 100644
--- a/src/utils.jl
+++ b/src/utils.jl
@@ -45,6 +45,9 @@ macro icxxdebug_str(str,args...)
     end
     push!(sourcebuffers,(takebuf_string(sourcebuf),args...))
     id = length(sourcebuffers)
+    println("Buffer: ")
+    println(sourcebuffers[id][1])
+    println("---------")
     esc(build_icxx_expr(id, exprs, isexprs, Any[], compiler, dumpast_impl))
 end

by the way, you can get the thing you want (I think) with: w = @cxx b2World(gravity) (ok, maybe sometimes don’t use icxx! :man_shrugging:)