Vector of objects (all fine) and vectors of pointers to objects (having problems) - problems accessing pointer members of these objects


#1

I am having trouble, iterating over a vector of pointers to objects, where each object itself has a pointer member that I am trying to access/query

To try to narrow it down as much as possible I execute exactly the same script:

  • First where I iterate over a vector of objects, and access the pointer member on the object (all fine)
  • Second, I iterate over a vector of pointers to objects, and am unable to access the pointer member.

By way of explanation, in the example code below (sorry it is not smaller)

  • CPerson class: simply serves as the inner member pointer I am looking to query
  • CItem class: simply wraps up a CPerson*
  • CContainerOfObjects class: stores a vector of CItem, i.e. std::vector, the script works fine when operating on one of these
  • CContainerOfPointers class: stores a vector of CItem*, i.e. std::vector<CItem*>, the script fails when operating on one of these

I am a bit lost. It has something do to with the inner pointer (the CPerson* that the CItem) wraps up not being handled (copy constructor related?) correctly.

The code is below, followed by the script that I am using.

We are trying to use Chaiscript inside an optimisation engine, and it will be great for this use case if we can get it going.

---------------------------------------------- CPP Code------------------------------

#include "chaiscript/chaiscript.hpp"
    
class CPerson
{
public:
	CPerson(const std::string& strName = "") {m_strName = strName;}
	CPerson(const CPerson& r) {m_strName = r.m_strName;}
	std::string GetName() const {return m_strName;}
private:
	std::string m_strName;
};

class CItem
{
public:
	CItem() {m_pPerson = nullptr;}
	CItem(CPerson* pPerson) {m_pPerson = pPerson;}
	CItem(const CItem& r) {m_pPerson = r.m_pPerson;}

	std::string StringMethod() const {return "StringMethodReturnValue";}
	std::string GetNameOfPersonFromPointerDirectly() const 
	{
		return m_pPerson != nullptr ? m_pPerson->GetName() : "null person";
	}
private:

	CPerson* m_pPerson{nullptr};
};

class CContainerOfObjects
{
public:
	void AddItem(const CItem& item) { m_vecItems.push_back(item); }
	const std::vector<CItem>& GetItems() const { return m_vecItems; }

private:
	std::vector<CItem> m_vecItems;
};

class CContainerOfPointers
{
public:
	void AddItem(CItem* pitem) { m_vecItems.push_back(pitem); }
	const std::vector<CItem*>& GetItems() const { return m_vecItems; }

private:
	std::vector<CItem*> m_vecItems;
};

using ContainerRule = std::function<bool(CContainerOfObjects)>;
using ContainerPointerRule = std::function<bool(CContainerOfPointers)>;

void Register(chaiscript::ChaiScript& chai)
{
	chaiscript::utility::add_class<CPerson>
	(
		chai, 
		"Person",
		{ 
			chaiscript::constructor<CPerson(const std::string&)>(),
			chaiscript::constructor<CPerson(const CPerson&)>()
		},
		{ 
			{chaiscript::fun(&CPerson::GetName), "GetName"}
		}
	);

	chaiscript::utility::add_class<CItem>
	(
		chai, 
		"Item",
		{ 
			chaiscript::constructor<CItem()>(),
			chaiscript::constructor<CItem(const CItem&)>(),
			chaiscript::constructor<CItem(CPerson*)>()
		},
		{ 
			{chaiscript::fun(&CItem::StringMethod), "StringMethod"},
			{chaiscript::fun(&CItem::GetNameOfPersonFromPointerDirectly), "GetNameOfPersonFromPointerDirectly"},
		}
	);
        
	chai.add(chaiscript::bootstrap::standard_library::vector_type<std::vector<CItem>>("ItemVector"));
	chai.add(chaiscript::bootstrap::standard_library::vector_type<std::vector<CItem*>>("ItemPointerVector"));
}


int main()
{
	CPerson person("James");

	auto iItem1 = CItem(&person);
	auto iItem2 = CItem(&person);

	try
	{
		// This will succeed
		{
			chaiscript::ChaiScript chai;
			try
			{
				Register(chai);
				chai.add(chaiscript::user_type<CContainerOfObjects>(), "Container");
				chai.add(chaiscript::fun(&CContainerOfObjects::GetItems), "GetItems");
				auto container_func = chai.eval_file<ContainerRule>("script.chai");

				auto cObjectContainer = CContainerOfObjects();
				cObjectContainer.AddItem(iItem1);
				cObjectContainer.AddItem(iItem2);
				bool fResult1 = container_func(cObjectContainer);
			}
			catch (const chaiscript::Boxed_Value &bv)
			{
				const auto& ex = chai.boxed_cast<const std::exception&>(bv);
				throw ex;
			}
		}

		std::cout << "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n";
                    // This will fail
		{
			chaiscript::ChaiScript chai;
			try
			{
				Register(chai);
				chai.add(chaiscript::user_type<CContainerOfPointers>(), "Container");
				chai.add(chaiscript::fun(&CContainerOfPointers::GetItems), "GetItems");
				auto container_func = chai.eval_file<ContainerPointerRule>("script.chai");

				auto cPointerContainer = CContainerOfPointers();
				cPointerContainer.AddItem(&iItem1);
				cPointerContainer.AddItem(&iItem2);
				bool fResult1 = container_func(cPointerContainer);
			}
			catch (const chaiscript::Boxed_Value &bv)
			{
				const auto& ex = chai.boxed_cast<const std::exception&>(bv);
				throw ex;
			}
		}
	}
	catch (const chaiscript::exception::bad_boxed_cast& e)
	{
		std::cout << "Error invoking script (cannot match types). Bad cast error: " << e.what();
	}
	catch (const chaiscript::exception::eval_error& e)
	{
		std::cout << "Error invoking script. Eval error: " << e.pretty_print();
	}
	catch (const chaiscript::exception::dispatch_error& e)
	{
		std::cout << "Error invoking script. Dispatch error: " << e.what();
	}
	catch (const std::exception& e)
	{
		std::cout << "Error invoking script. Exception error: " << e.what();
	}
	catch (...)
	{
		std::cout << "WTF";
	}

}

Below is the chaiscript I am trying to execute

fun(Container container) 
{ 
	auto items := container.GetItems();
	for (auto i = 0; i < items.size(); ++i) 
	{
		auto item := items[i];
		print("item.StringMethod()=" + item.StringMethod()); 
		print("item.GetNameOfPersonFromPointerDirectly()=" + item.GetNameOfPersonFromPointerDirectly()); // This will fail using CContainerOfPointers
	}
	return true;
}

#2

This appears to be an issue with the container returning a const reference to a vector of pointers. The pointed to objects in the vector are not initialized correctly.

Running the code with GetItems() returning a const value makes the bug go away.

Keen to hear from Jason if this is a bug in Chaiscript.


#3

This really looks like a lifetime issue, something is getting popped and destroyed before it should be, but I need to see how you are actually calling the function to know where the issue is coming from. Can you give a completely runnable minimized repro with only the code necessary for executing?

Thank you


#4

Oh, sorry I see now how you are executing the code, I’ll take a closer look.


#5

This issue is now fixed on the release-6.x branch and on develop. I see from your code samples that you are still using something before 6.x I strongly recommend upgrading to 6.1. There have been many bug fixes and performance improvements since then.