-
-
Notifications
You must be signed in to change notification settings - Fork 537
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SOL_SAFE_FUNCTION_CALLS does not recognize derived classes #1613
Comments
You should make
Also seems like in the example the |
That's rather unfortunate as I'm wrapping an API that uses smart pointers and not raw pointers and I have no control over it,
Right, it was just a quick example I whipped up and accidentally forgot the I don't get it though, does it mean that shared_ptr support in sol is completely useless? If I have a factory function such as the example in my original post, and say I wanted to store the Lua-created instances in a vector on the C++ side, there would be no way to take a raw pointer and store it as shared_ptr to keep the object alive. Given the following modified example, what would be the idiomatic way to achieve this without triggering UB? #include <cstdio>
#include <memory>
#include <vector>
#define SOL_SAFE_FUNCTION_CALLS 0
#include <sol/sol.hpp>
struct Animal {
virtual void say() {
std::printf("* quiet *\n");
}
};
struct Dog: public Animal {
void say() final {
std::printf("* woof *\n");
}
};
static void accept(std::shared_ptr<Animal> a)
{
static std::vector<std::shared_ptr<Animal>> animals;
animals.push_back(a);
a->say();
};
int main(int, char **)
{
sol::state state;
state.new_usertype<Animal>("Animal",
sol::factories([]() {
return std::make_shared<Animal>();
})
);
state.new_usertype<Dog>("Dog",
sol::factories([] {
return std::make_shared<Dog>();
}),
sol::base_classes, sol::bases<Animal>()
);
state["accept"] = &accept;
state.script("accept(Dog.new())");
return 0;
}; |
I was thinking about adding a wrapper that takes a raw pointer, then use a Allocating with new in the factory function will result in a memory leak: #include <cstdio>
#include <memory>
#include <vector>
#define SOL_SAFE_FUNCTION_CALLS 0
#include <sol/sol.hpp>
struct Animal {
virtual ~Animal() {
std::printf("~Animal();\n");
}
virtual void say() {
std::printf("* quiet *\n");
}
};
struct Dog: public Animal {
~Dog() {
std::printf("~Dog();\n");
}
void say() final {
std::printf("* woof *\n");
}
};
static void accept(std::shared_ptr<Animal> a)
{
static std::vector<std::shared_ptr<Animal>> animals;
animals.push_back(a);
a->say();
};
int main(int, char **)
{
sol::state state;
state.open_libraries(sol::lib::base);
state.new_usertype<Animal>("Animal",
sol::factories([]() {
return new Animal;
})
);
state.new_usertype<Dog>("Dog",
sol::factories([]() {
return new Dog;
}),
sol::base_classes, sol::bases<Animal>()
);
state["accept"] = [] (Animal *animal) {
std::shared_ptr<Animal> owned_animal { animal };
accept(owned_animal);
};
state.script("accept(Dog.new())");
state.script("Dog.new()");
state.script("collectgarbage('collect');");
return 0;
}; Allocating using the default Sol-owned memory will result in a double free: #include <cstdio>
#include <memory>
#include <vector>
#define SOL_SAFE_FUNCTION_CALLS 0
#include <sol/sol.hpp>
struct Animal {
virtual ~Animal() {
std::printf("~Animal();\n");
}
virtual void say() {
std::printf("* quiet *\n");
}
};
struct Dog: public Animal {
~Dog() {
std::printf("~Dog();\n");
}
void say() final {
std::printf("* woof *\n");
}
};
static void accept(std::shared_ptr<Animal> a)
{
static std::vector<std::shared_ptr<Animal>> animals;
animals.push_back(a);
a->say();
};
int main(int, char **)
{
sol::state state;
state.open_libraries(sol::lib::base);
state.new_usertype<Animal>("Animal"/*,
sol::factories([]() {
return new Animal;
})*/
);
state.new_usertype<Dog>("Dog",
/*sol::factories([]() {
return new Dog;
}),*/
sol::base_classes, sol::bases<Animal>()
);
state["accept"] = [] (Animal *animal) {
std::shared_ptr<Animal> owned_animal { animal };
accept(owned_animal);
};
state.script("accept(Dog.new())");
state.script("Dog.new()");
state.script("collectgarbage('collect');");
return 0;
}; |
One possibility is to use static void accept(std::variant<std::shared_ptr<Animal>, std::shared_ptr<Dog>> v)
{
static std::vector<std::shared_ptr<Animal>> animals;
std::visit([&](auto&& a)
{
animals.push_back(a);
a->say();
}, v);
};
state["accept"] = &accept; Or static void accept(std::shared_ptr<Animal> a)
{
static std::vector<std::shared_ptr<Animal>> animals;
animals.push_back(a);
a->say();
};
state["accept"] = sol::overload(
[](std::shared_ptr<Dog> a){ return accept(a); },
[](std::shared_ptr<Cat> a){ return accept(a); }
); In similar way you could also get in a The downside is obviously that you are required to list all of the types you need the function to be able to process in all of these solutions. Could be there are other ways to get around the issue. 🤔 |
I guess I will have to go with either of these, thanks, I'm closing this since I guess it's intended behavior. |
With the
SOL_SAFE_FUNCTION_CALLS
, I'd expect the type checker to actually allow inherited classes to be passed to a base pointer argument.But instead, the following code:
Produces the following error:
sol: runtime error: stack index 1, expected userdata, received sol.sol::d::u<Dog>: value is a userdata but is not the correct unique usertype (bad argument into 'void(std::shared_ptr<Animal>)')
The text was updated successfully, but these errors were encountered: