No interaction with base_class and type_conversion


#1

I have a component based scene graph in which a node class can have a derived component added. This derived component is stored in a shared_ptr, but contains no shared_ptr to its parent.

When storing a component I want to extend the lifespan of the node that owns it as well, so my “getComponent” methods return something called a “SafeComponent<T>” which contains both a shared_ptr to the component, and one to the component’s parent node. In this way users can be sure that when they have a SafeComponent handle the node won’t be yanked away while the component still lives.

In short, I have an object called SafeComponent which I want to treat transparently as a Component, its only function is to extend the lifespan of a parent node. I overload the dereference operators to return the shared_ptr, and I want to treat them the same in chaiscript as I do in C++.

In chaiscript I am okay with getting this behavior through simple casting (so I don’t have to explicitly hook up both the ComponentType and the SafeComponent which would be near identical. So my strategy was to use:

script.add(chaiscript::type_conversion<SafeComponent<MyComponent>, std::shared_ptr<MyComponent>>([](const SafeComponent<MyComponent> &t_bt) { return t_bt.wrapped; }));

But this isn’t exactly getting me what I want.

#include <memory>
#include <vector>
#include <functional>
#include <iostream>
#include <algorithm>

#include "chaiscript/chaiscript.hpp"
#include "chaiscript/chaiscript_stdlib.hpp"

class BaseComponent {
public:
	virtual ~BaseComponent() = default;

	void base() {
		std::cout << "base" << std::endl;
	}

	virtual void over() {
		std::cout << "over" << std::endl;
	}
};

class MyComponent : public BaseComponent {
public:
	int i = 0;
	void print() {
		std::cout << i << std::endl;
	}

	void add() {
		i++;
	}

	virtual void over() {
		std::cout << "derived" << std::endl;
	}
};

template <typename T>
class SafeComponent {
public:
	std::shared_ptr<T> operator->() const {
		return wrapped;
	}
	std::shared_ptr<T> wrapped;
};

int main() {
	chaiscript::ChaiScript script(chaiscript::Std_Lib::library());
	script.add(chaiscript::user_type<BaseComponent>(), "BaseComponent");
	script.add(chaiscript::fun(&BaseComponent::over), "over");
	script.add(chaiscript::fun(&BaseComponent::base), "base");

	script.add(chaiscript::user_type<MyComponent>(), "MyComponent");
	script.add(chaiscript::base_class<BaseComponent, MyComponent>());
	script.add(chaiscript::fun(&MyComponent::print), "print");
	script.add(chaiscript::fun(&MyComponent::add), "add");

	auto sharedComponent = std::make_shared<MyComponent>();
	SafeComponent<MyComponent> safeComponent;
	safeComponent.wrapped = sharedComponent;
	script.add_global(chaiscript::Boxed_Value(sharedComponent), "component");
	script.add_global(chaiscript::Boxed_Value(safeComponent), "safeComponent");

	script.add(chaiscript::type_conversion<SafeComponent<MyComponent>, std::shared_ptr<MyComponent>>([](const SafeComponent<MyComponent> &t_bt) { return t_bt.wrapped; }));
	//script.add(chaiscript::type_conversion<SafeComponent<MyComponent>, std::shared_ptr<BaseComponent>>([](const SafeComponent<MyComponent> &t_bt) { return t_bt.wrapped; })); // does not help

	//This works:
	script.eval(R"(
		component.print()
		component.add()
		component.print()
		component.base()
		component.over()
	)");

	//This breaks when it gets to "base" and "over":
	script.eval(R"(
		safeComponent.print()
		safeComponent.add()
		safeComponent.print()
		safeComponent.base()
		safeComponent.over()
	)");

	safeComponent->add();
	safeComponent->print();

	//m->add(chaiscript::type_conversion<TestBaseType, Type2>([](const TestBaseType &t_bt) { return Type2(t_bt); }));
	return 0;
}

#2

To summarize: type_conversion works when trying to cast from one type to another, however, when doing so it will not allow you to directly access base_class exposed methods (only methods directly in the class you’re casting to).


#3

Whoopse! I fixed it!

script.add(chaiscript::type_conversion<SafeComponent<MyComponent>, std::shared_ptr<BaseComponent>>([](const SafeComponent<MyComponent> &t_bt) { return std::static_pointer_cast<BaseComponent>(t_bt.wrapped); }));

I was missing a static_pointer_cast and getting an exception, I thought it was something else! Works now!