GithubHelp home page GithubHelp logo

billyquith / ponder Goto Github PK

View Code? Open in Web Editor NEW
628.0 34.0 92.0 9.09 MB

C++ reflection library with Lua binding, and JSON and XML serialisation.

Home Page: http://billyquith.github.io/ponder/

License: Other

CMake 2.37% C++ 97.54% Shell 0.09%
c-plus-plus reflection library introspection cpp cpp-library reflection-library vcpkg lua-bindings camp

ponder's Introduction

Ponder

Linux & OSX: Build Status - Windows: Build status

Currents status: 3.2-alpha. API is unstable as features added/changed.

New: Version 3

V1 replaced Boost with C++11. V2 added Lua bindings. V3 refactored to remove some warts, ease future development, and re-add serialisation. API is evolving.

About

Ponder is a C++ multi-purpose reflection library. It provides an abstraction for most of the high-level concepts of C++: classes, enumerations, functions, properties.

C++1x supplies better support for compile-time reflection. Runtime reflection involves baking compile-time reflection into the executable so this can be used at runtime. Ponder presents a simple API for baking your objects so that they can be used at runtime. The baking can also, optionally, userdata and support for Lua scripting.

Features:

  • API to expose C++ objects.
  • Runtime API to create objects, call functions, and read and modify properties.
  • Automatic Lua binding generation.

Links

Build

Compilers supported. Requires C++17.

  • MSVC 2017+
  • Xcode 8+
  • GCC 7+
  • Clang 3.9+

Ponder uses CMake to generate project files which are then used to build the project (Build docs). E.g. Use make to build Ponder (but you can use any build system CMake supports):

git clone https://github.com/billyquith/ponder.git
cd ponder
mkdir build && cd build
cmake -G "Unix Makefiles" ..
make

Package Managers

Windows vcpkg:

vcpkg install ponder

History

Ponder is a fork of CAMP, which has been retired by the original authors. CAMP relies on Boost, and is pre-C++11. In Ponder, the Boost dependency has been removed, and instead, C++11 features are used. CAMP was developed by Technogerma Systems France and then by Tegesoft.

See CHANGELOG.md and release notes for more details on Ponder changes.

ponder's People

Contributors

antagna avatar billyquith avatar cofenberg avatar dehesselle avatar delaitre avatar drbenmorgan avatar fredericleuba avatar laurentgomila avatar leolchat avatar openhat-org avatar renindustries avatar shierei avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ponder's Issues

static_cast causes runtime exception when using virtual inheritance

If some class B is virtually derived from some class A

    class B : public virtual A

and class B is declared for ponder like

    ponder::Class::declare<B>("B")
        .base<A>()
        ...

a runtime exception is caused. It is caused in classbuilder.inl, where the offset to apply for pointer conversions is computed. There, the value (1) is tried to be cast to the base class. In our environment, this leads at runtime to an "access violation reading location 0x00000001". Our environment: MS VS2015.
Code line where the exception occurs:

U* asBase = static_cast<U*>(asDerived);

As a side note: Actually, we do not need virtual inheritence. Hence, the above problem fortunately does not really affect us. Once we recognized the problem, we just omitted the virtual inheritence.

Add way to generate C FFI

C++ can be difficult to inter-operate with. C is much more simple. Provide a way to use a declared C++ interface via C. This could then be used by things like Luajit and Julia.

Compile failed Ubuntu 14.04 - missing POSIX include

I think util.cpp missing

#include <strings.h>

Since I get

[ 92%] Building CXX object CMakeFiles/ponder.dir/src/util.cpp.o
/home/david/ponder/src/util.cpp: In function ‘int ponder::detail::stricmp(const char*, const char*)’:
/home/david/ponder/src/util.cpp:50:24: error: ‘strcasecmp’ was not declared in this scope
  return strcasecmp(a, b);
                        ^
make[2]: *** [CMakeFiles/ponder.dir/src/util.cpp.o] Error 1
make[1]: *** [CMakeFiles/ponder.dir/all] Error 2
make: *** [all] Error 2

Property value by index shortcuts

The shortcut UserObject::get(const std::string & property) is provided as a shortcut for object.getClass().property(name).get(object).

Would it be possible to provide an equivalent for indexed access to properties, i.e. UserObject::get(const size_t index)?

Justification: this helps when iterating over all properties in a class instance.

Probably it would be good to provide the set equivalent for completeness.

Registering a type with multiple template arguments fails

If you have a class with two template parameters, e.g.

template <typename T, typename U>
class TestClass
{
public:
    U foo(T param);
};

registering this class to ponder with e.g.

PONDER_TYPE(TestClass<int, int>);

fails. (In my environment, which is VS2015, I get

warning C4002: too many actual parameters for macro 'PONDER_TYPE'

error C2976: 'TestClass': too few template arguments

A possible fix could be to define PONDER_TYPE with a variadic macro like this:

#define PONDER_TYPE(...) \
    namespace ponder \
    { \
        namespace detail \
        { \
            template <> struct StaticTypeId<__VA_ARGS__> \
            { \
                static const char* get(bool = true) {return #__VA_ARGS__;} \
                enum {defined = true, copyable = true}; \
            }; \
        } \
    }

Add destructor function to user object holders

Currently UserObjects have to be manually destructed. Since they have shared_ptrs referencing the holders we should be able to automate this.

This could also be used for placement new, where destruct must be called instead of destroy.

Added experimented unique_ptr for UserObjects.

ponder_ext::ValueMapper not in the documentation?

The Valueclass documentation mentions that the standard set of supported types can be extended using ponder_ext::ValueMapper, but there is no hyperlink to this struct.

The source file valuemapper.hpp has a nice example for this great feature, so I think it would be good if this was linked in the Valuedocumentation.

Error handling policies

Subtopic moved from #12 because this refers to an enhancement whereas the rest were just questions.

Lastly, just one little request ... can we have asserts instead of exceptions please ?!!!

Not so little, but perhaps should be allowed. Perhaps "policies" could be introduced for error handling. You might pass one in so that your app can deal with errors however it chooses?

This sounds like an excellent idea.

Might I ask why you want errors handled as asserts?

Possibly this is due to my lack of C++ knowledge, so please correct me because I may completely misunderstand a very basic point about exceptions ...

I tend to use gcc / gdb on Linux and their MinGW64 ports on Windows.

The default behavior of gdb [seems to be] when it gets an unhandled exception to terminate with no message, no stack trace, no clue whatsoever. On the other hand an assert suspends the execution at exactly the place where the assert occurred and provides a call stack up to that point.

Perhaps, I should just be catching exceptions at the highest level, but I still don't think I will get a call stack to the line that caused the exception? Also I am using other libraries which have exceptions, then it is hard to know which library threw the exception.

So the result is that I have to wrap every Ponder call with try / catch, even if it is just one line. I guess that makes sense in production so as not to leave the program in an indeterminate state or with incorrect values but for debugging it is a bit of a nightmare ;-). Even in production, one missed try / catch could cause sudden, unexplained program closure?

Support for array_view/span-like arrays?

If I have a class that holds an array represented as a pointer and its length like e.g.

template <typename T>
struct PodArray
{
    T* parray_;
    std::size_t size_;
};

PONDER_TYPE(PodArray<uint8_t>)

then I would like to be able to declare an array property for this class e.g. in the following way

    ponder::Class::declare<PodArray<uint8_t>>()
        .property("Array", &PodArray::parray_, &PodArray::size_);

If I get it right then the resulting array property would (in the first place) have to be non-dynamic since in your current implementation ArrayProperty::setSize() does an element-wise insertElement() to grow the array.

I made first attempts to implement such functionality, but since I am no C++ template expert this is hard for me because of all the compile time distinction of cases with PropertyFactory[n], Accessor[n] and the like.

When I think of an array represented as a pointer and its length I think of it in terms of an array_view<T> or a span<T>. Maybe a possible implementation for this issue could be based on a specialization of ArrayMapper for an array_view<T>. Unfortunately, a standardized class array_view is currently not yet available. Nevertheless, there are current implementations, like this one on GitHub: array_view.


Side note for future extension: Since our domain is image processing, I would of course be interested in an ArrayProperty for arrays with more than one dimension (for image processing, particularly, a support for two-dimensional arrays would be welcome). To illustrate this point, I give a code example:

template <typename T>
struct Image
{
    T* pdata_;
    std::size_t width_;
    std::size_t height_;
};

PONDER_TYPE(Image<uint8_t>)
...
    ponder::Class::declare<Image<uint8_t>>()
        .property("Image", &PodArray::parray_, &PodArray::width_, &PodArray::height_);
...
   arrayProperty.get(obj, 2, 5); // get image value at position [2][5]

The class span<T> would be suited for multi-dimensional arrays (there, the number of dimensions is called rank). span<T> is part of the GSL (Guidelines Support Library Guidelines) and maintained by Microsoft (you find it here on GitHub: GSL). Alas, I could imagine that to support an ArrayProperty for more than one dimension the effort would be high (and maybe in confict with the concepts so far). Also, I do not know in which state the implementation of span<T> is (its quite huge, more than 2000 LOC) and how platform independent it is.


As always, I would be interested in your opinion and further plans. If you prefer I could try to continue with my attempts to implement such functionality (for the one-dimensional case).

Add return policies

Objects that return references are ambiguous. The reference could a value to copy, or it could be an internal reference. There is no way of knowing without a hint.

int value conversion for hex

Hi all,
I suggest a small enhancement which adds support for calling functions with hex or oct arguments, in using the std::stol function for that, by just changing the call in line 64 to
std::stol(from.c_str(),0,0), the third argument defines the base (default 10, 0 means interpreting strings with prefix 0x or 0X as hex and prefix 0 as octal).
Best, and thanks Billy for starting and maintaining this very useful library
Mat

Investigate MSVC 2015 compiler crash

Debug build.

c:\program files (x86)\microsoft visual studio 14.0\vc\include\type_traits(1416): fatal error C1001: An internal error has occurred in the compiler.
1>  (compiler file 'f:\dd\vctools\compiler\utc\src\p2\main.c', line 246)
1>   To work around this problem, try simplifying or changing the program near the locations listed above.
1>  Please choose the Technical Support command on the Visual C++
1>   Help menu, or open the Technical Support help file for more information

apparently related to:

template<class _Callable,
    class... _Types> inline
    auto invoke(_Callable&& _Obj, _Types&&... _Args)
    -> decltype(_Invoker<_Callable, _Types...>::_Call(
        _STD forward<_Callable>(_Obj), _STD forward<_Types>(_Args)...))
    {   // INVOKE a callable object
    return (_Invoker<_Callable, _Types...>::_Call(
        _STD forward<_Callable>(_Obj), _STD forward<_Types>(_Args)...));
    }

in <type_traits>.

Declaring templated class to ponder fails

If I have some template class e.g.

template <class T>
class CTestClass
{
public:
    T testMember_;
};

and like to declare this class to ponder with e.g.

PONDER_TYPE(CTestClass<int>)

void declare_CTestClass()
{
    ponder::Class::declare<CTestClass<int>>("CIntTestClass")
        .constructor();
        .property("TestMember", &CTesteClass<int>::testMember_);
}

I get several compiler errors (I can post you details if you think it is necessary).

To me it seems that the reason for the failure is detail::IsSmartPointer: detail::IsSmartPointer erroneously detects my templated class as a smart pointer, hence parts of ponder assume that the desired type is int rather than CTestClass<int>. This leads to compiler errors of the kind that there is no conversion between CTestClass<int>* and int*.

Can ponder be used to build a generic data repository?

We consider building a generic data repository (GDR) in which entries are stored in the following manner:

struct Entry {
    const ponder::Class& metaClass;
    void* p;
}

If we have created some object of class T (with T being a class declared to ponder)

T* pObject = ...

we would store it in the GDR like this:

someEntry.p = pObject;
someEntry.metaClass = ponder::classByObject(*pObject);

If that would work we could examine our GDR in a very generic way by initializing a ponder::UserObject for each entry and working with that ponder::UserObject through the reflection capabilities of ponder.

Alas, for such a generic data repository to work some obstacles would have to be overcome.

First of all, there currently is no functionality to initialize a ponder::UserObject from a ponder::Class and a void* pointer. (I'd guess this would be doable.)

Second, we would also like to store PODs, std::string, std::array, std::vector and the like in the GDR. For those types, there is no direct support in ponder, i.e. it is not possible to simply declare those types to ponder and initialize a ponder::UserObject for it. One way would be to wrap those types into custom classes like

template <typename T>
struct PodWrapper
{
    T value;
};
PONDER_TYPE(PodWrapper<uint8_t>)
PONDER_TYPE(PodWrapper<uint16_t>)
...
ponder::Class::declare<PodWrapper<uint8_t>>("PodWrapper<uint8_t>")
    .constructor()
    .property("Value", &PodWrapper<uint8_t>::value);
ponder::Class::declare<PodWrapper<uint16_t>>("PodWrapper<uint16_t>")
    .constructor()
    .property("Value", &PodWrapper<uint16_t>::value);
...    

but maybe there is some better way.

I would be glad to hear your opinion on the subject of this issue.

Unresolved externals

Hey,
I compiled ponder with MSVC2015 (generated project using the CMAKE file provided).

In my project I set up the usual library linking paths and included ponder.lib and followed the code from the example code provided but I am getting unresolved external symbols (crazy big errors from VS). I'm fairly certain I have linked correctly (checked this over and over) but it doesn't seem to be linking.

Could this be a build issue?

Image of the errors: https://gyazo.com/29f2253c683a50b704c388afaa6b972d

Class header file: https://gyazo.com/95f69c5bce8fce24b45d11f9622b568c
Class source file: https://gyazo.com/a591b22d2312e1936b81f4d40257a40e
Main : https://gyazo.com/c9b11549ce0de44a72dc77f79ab9c53e

I've tried various things even included all the headers into my project incase their was some inline methods that were needed but still couldn't get it work.

Really want to use this library, I have been looking for a few days for a nice c++ reflection library and this seems perfect for my needs :)

Problems with ponder::Value and std::map

I am using TDM MinGW64 on Windows 10.

There is a problem when a std::map containing ponder::Value objects goes out of focus. It calls the dtor for the Value objects, but the dtor seems to have problems freeing the memory.

Here is a simple test case to reproduce the problem:

    try
    {
        std::map<std::string, ponder::Value> testmap;

        ponder::Value value = "Hello";

        testmap["1"] = value;

        std::cout << "Testing: " << testmap["1"] << std::endl;

        // Error when testmap goes out of focus here.
    }
    catch (ponder::Error e)
    {
        std::cout << "Error: " << e.what() << std::endl;
    }

And here is the back trace that leads to the SIGTRAP

0  0x00007ffda889e69c  ntdll!RtlpNtMakeTemporaryKey    
1  0x00007ffda88a0d86  ntdll!RtlpNtMakeTemporaryKey    
2  0x00007ffda8854b9a  ntdll!RtlRaiseStatus    
3  0x00007ffda87d08f9  ntdll!RtlFreeHeap    
4  0x00007ffda6549b9c  msvcrt!free    
5  0x0000000000439bc3  mapbox::util::variant_helper<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, ponder::EnumObject, ponder::UserObject>::destroy  C:\\Users\\iwbnwif\\Development\\general_tests\\ponder\\ponder\\include\\ponder\\detail\\variant.hpp  211
6  0x0000000000439cf7  mapbox::util::variant_helper<double, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, ponder::EnumObject, ponder::UserObject>::destroy  C:\\Users\\iwbnwif\\Development\\general_tests\\ponder\\ponder\\include\\ponder\\detail\\variant.hpp  215
7  0x0000000000439d87  mapbox::util::variant_helper<long, double, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, ponder::EnumObject, ponder::UserObject>::destroy  C:\\Users\\iwbnwif\\Development\\general_tests\\ponder\\ponder\\include\\ponder\\detail\\variant.hpp  215
8  0x0000000000439c67  mapbox::util::variant_helper<bool, long, double, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, ponder::EnumObject, ponder::UserObject>::destroy  C:\\Users\\iwbnwif\\Development\\general_tests\\ponder\\ponder\\include\\ponder\\detail\\variant.hpp  215
9  0x0000000000439b07  mapbox::util::variant_helper<ponder::NoType, bool, long, double, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, ponder::EnumObject, ponder::UserObject>::destroy  C:\\Users\\iwbnwif\\Development\\general_tests\\ponder\\ponder\\include\\ponder\\detail\\variant.hpp  215
10  0x000000000043f143  mapbox::util::variant<ponder::NoType, bool, long, double, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, ponder::EnumObject, ponder::UserObject>::~variant  C:\\Users\\iwbnwif\\Development\\general_tests\\ponder\\ponder\\include\\ponder\\detail\\variant.hpp  794
11  0x00000000004506c8  ponder::Value::~Value  C:\\Users\\iwbnwif\\Development\\general_tests\\ponder\\ponder\\include\\ponder\\value.hpp  74
12  0x00000000004ca1dc  std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, ponder::Value>::~pair  C:\\TDM-GCC-64\\lib\\gcc\\x86_64-w64-mingw32\\5.1.0\\include\\c++\\bits\\stl_pair.h  96

Therefore I think that this is actually a mapbox::variant problem and not specific to Ponder.

For reference, the following works as expected:

    try
    {
        std::unordered_map<std::string, ponder::Value> testmap;

        ponder::Value value = 5; //"Hello";

        testmap["1"] = value;

        std::cout << "Testing: " << testmap["1"] << std::endl;

        // Works fine
    }
    catch (ponder::Error e)
    {
        std::cout << "Error: " << e.what() << std::endl;
    }

VS2015 failed to compile array properties if element type is smart pointer

I was encountered with compile errors when compiling the following code with Visual Studio 2015. Looks like it happens when a property is an array of smart pointers.

struct Element
{
};

struct Container
{
    std::vector<std::shared_ptr<Element>> elements;
};

PONDER_TYPE( Element );
PONDER_TYPE( Container );

ponder::Class::declare<Element>("Element")
    .constructor<>()
;
ponder::Class::declare<Container>("Container")
    .constructor<>()
    .property("elements", &Container::elements)      // compile error !!
;

Using as a component in a larger project

Normally, with CMake, we simply set a few options and then add an ADD_SUBDIRECTORY

Is there any way to use this in such a way? I'm attempting to use it along with GWork, but it doesn't seem to be working well.

Add Windows CI

Use Appveyor to support Windows CI. Need:

  • static and dynamic libraries.
  • debug and release.

[Question] camp-lua equivalent?

Hi! Back in the days of Tegesoft, there was an extension/plugin for Camp that bound it and Lua together. Is it still possible to do with Ponder? Anybody still has the source of this project or could help me do it (I'm not an expert with Lua)?
Thanks

Enhanced support for arrays?

Would it be possible to extend ponder::Value to fully support arrays?

Currently, arrays (T[], std::vector, std::list) are supported as properties of classes. Alas, ponder::Value does not support arrays, i.e. it cannot hold an array. In my opinion, this has the following drawbacks:

  • I cannot declare a function to ponder that expects an array as parameter.
  • When I want to serialize an array, I have to do it value per value (through functionality of ponder::ArrayProperty). (Our domain is image processing where one has large arrays, making value per value serialization too expensive.)
  • If I have a class with an array property, calling ponder::Property::get(object) or ponder::Property::set(object, value), respectively, gets or sets the first element of the array (which is counterintuitive in my opinion).

I don't know what the best way would be for ponder::Value to support arrays. I could imagine that, once ponder::Value supports arrays, ponder::Property::get(object) and ponder::Property::set(object, value) work with the array rather with the first element. The value per value approach would still be possible through ponder::ArrayProperty.

In one of my earlier comments I wrote that maybe we try to use ponder for something it was not designed for. Therefore, I am curious about your opinion and plans regarding my recent issues. Thank you in advance.

Basic usage questions

Hi, I would like to use Ponder to replace my home grown reflection that is currently implemented using nested macros and which suffers from all the debugging problems you mention in your blog :)

I need to warn you that I am relatively inexperienced in C++, so there may be some basic concepts I am missing.

  1. In a relatively complex project, which is split across several DLL, where is the best place to perform the binding (i.e. ponder::Class::declare<Person>("Person")...) ? I was wondering if I could create a composite container that holds my class and the binding within a static "Init" method. Thus, the binding will occur at startup and be already available whenever I instantiate the superclass.
  2. Is there a way for a subclass whose parent has been bound to Ponder to 'inherit' that binding? For example, if I were to subclass Person as Employee and Customer, do I have to rebind &Employee::name, &Employee::age, &Employee::speak, &Customer::name, &Customer::age, &Customer::speak etc. I tried to see if this possible without binding subclasses, but get a property age not found exception. With my macros I am using a lambda to extend the static bind map of the base class.
  3. I would like to be able to add extra meta data to properties for use when displaying the object, specifically "Hidden" and "Width". I think it would be convenient and efficient if I could do this as part of the binding call e.g. .property("name", &Person::name, visible, 200). I think this should be possible by subclassing ponder::UserProperty but would really appreciate if you could give me an example.

Lastly, just one little request ... can we have asserts instead of exceptions please ?!!!

Thanks for a great library, with a great licence 👍

Lua binding generator

Generate a Lua (5.2) binding from a declared API. This is currently a WIP in the "script" branch. Hoping to include in version 1.4.

Currently can create class instances, access properties, call methods. Performance not currently a priority, just functionality.

Change ponder::Type to enum class

enum Type
{
    noType,     ///< No type has been defined yet
    boolType,   ///< Boolean type (bool)
    intType,    ///< Integer types (unsigned/signed char short int long)
    realType,   ///< Real types (float, double)
    stringType, ///< String types (char*, std::string)
    enumType,   ///< Enumerated types
    arrayType,  ///< Array types (std::vector, std::list, T[])
    userType    ///< User-defined classes
};

to:

enum class Type
{
    none,   ///< No type has been defined yet
    bool,   ///< Boolean type (bool)
    int,    ///< Integer types (unsigned/signed char short int long)
    real,   ///< Real types (float, double)
    string, ///< String types (char*, std::string)
    enum,   ///< Enumerated types
    array,  ///< Array types (std::vector, std::list, T[])
    user    ///< User-defined classes
};

UserObject does not enforce dynamic type safety when converting within class hierarchy.

I've been experimenting with ponder to decide whether to use it for an upcoming project, and have run into a major problem: missing dynamic type safety when converting types within a class hierarchy.

Given a derived class (Derived) inheriting from a base class (Base), constructing a ponder::UserObject of class Base allows it nonetheless to be gotten as a Derived.

#include <ponder/classbuilder.hpp>
#include <ponder/pondertype.hpp>

#include <iostream>

class Base {
public:
    Base() { std::cout << "Base():" << this << std::endl; }
    virtual ~Base() { std::cout << "~Base():" << this << std::endl; }
};

PONDER_TYPE(Base)

class Derived : public Base {
public:
    Derived() {
        std::cout << "Derived():" << this << std::endl;
    }
    Derived(Derived const &other) {
        std::cout << "Derived(Derived const &" << &other << "):" << this << std::endl;
    }
};

PONDER_TYPE(Derived)

int main() {
    ponder::Class::declare<Base>("Base")
        .constructor<>()
        .constructor<Base const &>();

    ponder::Class::declare<Derived>("Derived")
        .constructor<>()
        .constructor<Derived const &>()
        .base<Base>();

    ponder::Class const &metaclass = ponder::classByName("Base");
    ponder::UserObject obj = metaclass.construct();

    // ISSUE: The following line calls Derived's copy constructor but passing the Base instance.
    Derived derived = obj.get<Derived>();

    metaclass.destroy(obj);
}

Support references to pondered objects when calling functions?

Consider that you have some class A holding some data

class ClassA
{
public:
    ClassA() : TestMember(0) {};

    int TestMember;
};

and a second class B that is able to modify the data in class A

class ClassB
{
public:
    void ModifyA(ClassA& obj) { obj.TestMember = 5; }
};

Then you could declare those classes to ponder like this:

PONDER_TYPE(ClassA)
PONDER_TYPE(ClassB)

void declare_Classes()
{
    ponder::Class::declare<ClassA>("ClassA")
        .constructor()
        .property("TestMember", &ClassA::TestMember);
    ponder::Class::declare<ClassB>("ClassB")
        .constructor()
        .function("ModifyA", &ClassB::ModifyA);
}

Now consider the following code where function ModifyA() is called on an object of class A with the intend to modify that object:

    ClassA objectA;
    const ponder::Class& metaClassB = ponder::classByName("ClassB");
    ponder::UserObject wrapperB = metaClassB.construct();
    const ponder::Function& functionB = metaClassB.function("ModifyA");

    // Try to modify data in object A --> this does not work
    functionB.call(wrapperB, objectA);

    metaClassB.destroy(wrapperB);

This code does not work because the object that is passed to function ModifyA() is not a reference to objectA, rather it is a reference to a temporary copy of objectA.

What is your opinion? Should it be possible to pass references to objects (that have been declared to ponder) to functions?

(A workaround for the above problem would be to change ModifyA to

void ModifyA(const ponder::UserObject& obj)
{
    ClassA& objectA = obj.get<ClassA&>();
    objectA.TestMember = 5;
}

and pass UserObject(objectA) to ModifyA, but I don't consider this to be nice.)

Support for static functions

Currently Ponder does not support class static and non-class functions. It would be nice to have this. The current function implementation is really a method.

  • Rename class functions to method?
  • Allow namespacing?

Support for creating objects in preallocated memory (catchphrase "placement new")

We would like to be able to create objects with ponder in preallocated memory. We would like this to work for objects for which we know just the class name but not the class definition (i.e. we do not have the class's header file available). For this to work, we would need the following functionalities:

  • Construct() calls which we could pass a pointer to some preallocated memory, which internally constructs the desired object with placement new
  • Some function with which we could get the size of the object (such that we can placement-new other objects adjacently in our preallocated memory)
  • When a placement-new-constructed object is to be released, only its deconstructor should be called but the memory must not be deleted

Would it be possible to extend ponder with the above functionalities?

Unwanted implicit conversion in constructor marked explicit

This might be better on StackOverflow, but as it directly relates to Ponder I thought I would try here first.

I have a class (actually a dialog box) that users Ponder to display an object.

The ctor declaration looks something like this:
explicit MyDataObjectDialog (..., const ponder::UserObject& object, ....);

Where a member reference variable m_Object is initialized with object in the ctor's base initializer list.

I was facing a problem when I accidentally called the ctor like this:

// Causes crash in userobject.cpp because m_holder isn't valid?
myObject object;
MyDataObjectDialog dialog (..., object, ...);

... show dialog ...
... access updated object ...

// Works fine
myObject object;
ponder::UserObject uObject (object);
MyDataObjectDialog dialog (..., uObject, ...);

... show dialog ...
... access updated object ...

I believe this is because the ponder::UserObject created in the first call is temporary and goes out of scope once the ctor is finished. This is all quite understandable.

My question is why myObject is [implicitly] being accepted by the explicit ctor that is expecting a const ponder::UserObject&? Also if this sort of conversion is valid, shouldn't Ponder be able to handle it?

This is on Linux using 4.8.4.

Serializing Ponder Objects (XML / JSON)

Hi, thank you for your continued efforts on this project.

There is a subfolder include/ponder-xml but as far as I can tell these features are not included in the documentation.

Actually, the methods serializeand deserialize (with the RapidXML compatibility layer) appear to do exactly what I need, but it would be even better if there was a RapidJSON alternative!

Do you think it would be possible to:

  1. Include the serialization methods in the documentation.
  2. Provide a small round-trip example.
  3. Consider an enhancement to also provide a [Rapid]JSON compatibility layer.

Support for setters that return a reference to the object (catch phrase "named parameter idiom")

If you have some class with some property where the setter returns a reference to the object (which is known as the "named parameter idiom", see e.g. Method chaining),

class MyClass
{
public:
    int Getter() { return i_; };
    MyClass& Setter(int i) { i_ = i; return *this; };
private:
    int i_;
};

and you like to declare this class to ponder with

PONDER_TYPE(MyClass)

void declare_MyClass()
{
    ponder::Class::declare<MyClass>("MyClass")
        .constructor()
        .property("Value", &MyClass::Getter, &MyClass::Setter);
}

you get several compile errors. These are caused by the fact that, in propertyfactory.hpp, Acessor3 is chosen instead of Acessor2.

I suggest to explicitly check for the "named parameter idiom" in the template specialication for Acessor3 (where now it is only checked whether the F2 return type is void).

Extend objects in runtime

Hi,

How difficult will be to add to ponder the possibility to expand objects with new attributes in runtime? For example:

const ponder::Class& metaclass = ponder:: classByName( "Person" );

metaclass.extension_property( "salary", double  );

ponder::UserObject john = metaclass.construct(ponder::Args("John"));
john.set( "salary", 100000 );

Rename PONDER_RTTI

This macro is used to allow dynamic casting within a class hierarchy. Docs:

Macro used to activate the Ponder RTTI system into a hierarchy of classes

The name therefore seems a little misleading. Suggestions?

Perhaps PONDER_DYNAMIC?

UserObject function that returns a pointer to the wrapped object

We would like UserObject to have an addtional function similar to get() that returns a pointer to the wrapped object rather than a reference (e.g. a function getPointer()).

Motivation: At initialization time where performance is not key we like - with the help of ponder - to prepare a data repository and some worker objects that work with the data objects in that repository. We would like to initialize those worker objects such that they store some kind of reference to the data objects. During processing time where performance is key the worker objects should have fast access to the data objects by way of the stored references.

We consider wrapping our data objects in UserObjects. Now, the only way to get some reference to the wrapped object is by way of get() which returns a reference T&. Alas, this reference cannot easily be stored for later use in the processing phase (and we would not like to use a std::reference_wrapper). Hence, we would like to have a function that returns a pointer to the wrapped object and the returned pointer we can store for fast access during processing time.

linking pondertest.exe on Windows using msys/mingw fails

I'm using mingw-w64 on msys2 and cmake -G "Unix Makefiles". Then make fails to link pondertest.exe:

...
[100%] Linking CXX executable pondertest.exe
CMakeFiles/pondertest.dir/arrayproperty.cpp.o:arrayproperty.cpp:(.text$_ZN10ponder_ext11ValueMapperIN6ponder9ValueTypeEvE2toB5cxx11ES2_[_ZN10ponder_ext11ValueMapperIN6ponder9ValueTypeEvE2toB5cxx11ES2_]+0x27): undefined reference to `ponder::detail::typeAsString(ponder::ValueType)'
CMakeFiles/pondertest.dir/traits.cpp.o:traits.cpp:(.text+0x9e7e): undefined reference to `ponder::detail::typeAsString(ponder::ValueType)'
CMakeFiles/pondertest.dir/traits.cpp.o:traits.cpp:(.text+0x9f92): undefined reference to `ponder::detail::typeAsString(ponder::ValueType)'
CMakeFiles/pondertest.dir/traits.cpp.o:traits.cpp:(.text+0xa0a6): undefined reference to `ponder::detail::typeAsString(ponder::ValueType)'
collect2.exe: error: ld returned 1 exit status
make[2]: *** [test/CMakeFiles/pondertest.dir/build.make:720: test/pondertest.exe] Fehler 1
make[1]: *** [CMakeFiles/Makefile2:123: test/CMakeFiles/pondertest.dir/all] Fehler 2
make: *** [Makefile:161: all] Fehler 2

The contents of ${MY_BUILD_DIR}/test/CMakeFiles/pondertest.dir/link.txt is:

/mingw64/bin/c++.exe     CMakeFiles/pondertest.dir/arrayproperty.cpp.o CMakeFiles/pondertest.dir/class.cpp.o CMakeFiles/pondertest.dir/classvisitor.cpp.o CMakeFiles/pondertest.dir/constructor.cpp.o CMakeFiles/pondertest.dir/dictionary.cpp.o CMakeFiles/pondertest.dir/enum.cpp.o CMakeFiles/pondertest.dir/enumclass.cpp.o CMakeFiles/pondertest.dir/enumclassobject.cpp.o CMakeFiles/pondertest.dir/enumclassproperty.cpp.o CMakeFiles/pondertest.dir/enumobject.cpp.o CMakeFiles/pondertest.dir/enumproperty.cpp.o CMakeFiles/pondertest.dir/example_mainpage.cpp.o CMakeFiles/pondertest.dir/function.cpp.o CMakeFiles/pondertest.dir/functionaccess.cpp.o CMakeFiles/pondertest.dir/inheritance.cpp.o CMakeFiles/pondertest.dir/main.cpp.o CMakeFiles/pondertest.dir/mapper.cpp.o CMakeFiles/pondertest.dir/property.cpp.o CMakeFiles/pondertest.dir/propertyaccess.cpp.o CMakeFiles/pondertest.dir/string_view.cpp.o CMakeFiles/pondertest.dir/tagholder.cpp.o CMakeFiles/pondertest.dir/traits.cpp.o CMakeFiles/pondertest.dir/userobject.cpp.o CMakeFiles/pondertest.dir/userproperty.cpp.o CMakeFiles/pondertest.dir/value.cpp.o  -o pondertest.exe -Wl,--out-implib,libpondertest.dll.a -Wl,--major-image-version,0,--minor-image-version,0  ../libponder.dll.a

I can fix the build by replacing ../libponder.dll.awith ../msys-ponder.dll and adding ../CMakeFiles/ponder.dir/src/util.cpp.o just before -o pondertest.exe:

/mingw64/bin/c++.exe     CMakeFiles/pondertest.dir/arrayproperty.cpp.o CMakeFiles/pondertest.dir/class.cpp.o CMakeFiles/pondertest.dir/classvisitor.cpp.o CMakeFiles/pondertest.dir/constructor.cpp.o CMakeFiles/pondertest.dir/dictionary.cpp.o CMakeFiles/pondertest.dir/enum.cpp.o CMakeFiles/pondertest.dir/enumclass.cpp.o CMakeFiles/pondertest.dir/enumclassobject.cpp.o CMakeFiles/pondertest.dir/enumclassproperty.cpp.o CMakeFiles/pondertest.dir/enumobject.cpp.o CMakeFiles/pondertest.dir/enumproperty.cpp.o CMakeFiles/pondertest.dir/example_mainpage.cpp.o CMakeFiles/pondertest.dir/function.cpp.o CMakeFiles/pondertest.dir/functionaccess.cpp.o CMakeFiles/pondertest.dir/inheritance.cpp.o CMakeFiles/pondertest.dir/main.cpp.o CMakeFiles/pondertest.dir/mapper.cpp.o CMakeFiles/pondertest.dir/property.cpp.o CMakeFiles/pondertest.dir/propertyaccess.cpp.o CMakeFiles/pondertest.dir/string_view.cpp.o CMakeFiles/pondertest.dir/tagholder.cpp.o CMakeFiles/pondertest.dir/traits.cpp.o CMakeFiles/pondertest.dir/userobject.cpp.o CMakeFiles/pondertest.dir/userproperty.cpp.o CMakeFiles/pondertest.dir/value.cpp.o ../CMakeFiles/ponder.dir/src/util.cpp.o  -o pondertest.exe -Wl,--out-implib,libpondertest.dll.a -Wl,--major-image-version,0,--minor-image-version,0  ../msys-ponder.dll

But I'm out of my depth here, I don't understand why I had to link in ../CMakeFiles/ponder.dir/src/util.cpp.o- isn't this object supposed to be supplied by the DLL (I checked ${MY_BUILD_DIR}/CMakeFiles/ponder.dir/link.txt and it's there)?

Possibility to undeclare types?

We consider using ponder in the following way: Assume that our application should be extensible. Further assume that extension clases should derive from a base class that is declared by our application. We would like to use the extension classes in connection with ponder in the following way:

  • Load some dll containing an extension class. When the dll is loaded, the dll has to declare the extension class to ponder.
  • Knowing the extension class's ponder name (through some meta information like an XML file containing a description for the dll) we let ponder construct an object of this class and cast it to our base class. We make use of this object by calling the base class's virtual interface functions.
  • After we are finished using that extension object, we would like to destroy it and unload the dll.
    When we work as described, we get an exception when our application exits. (We do not get the exception at application exit if we do not unload the dll.) It seems to us that in the destruction code of ponder it is tried to destruct something that has recided in the dll but is now not anymore available since the dll has been unloaded.
    What could be a solution to this problem? Is there the need for the possibility to explicitly undeclare types?

needless memory allocations

Ponder seems to make many needless allocations. For a first example, let's look at the .property member of ClassBuilder:

 ClassBuilder<T>& ClassBuilder<T>::property(const std::string& name, F1 accessor1, F2 accessor2)

Note that it takes a std::string for the property name. Typical uses here will always be using a string literal in the reflection declaration, however. The official example:

ponder::Class::declare<Person>("Person")
    .property("name", &Person::name);

Here, "name" will be copied (with allocation, sans any small-string optimization). Allocating and copying static strings is mostly unnecessary.

This is even more problematic in your query functions, e.g. Class::hasProperty. In that case, the std::string const& parameter requires a temporary allocation just to query data; the allocation serves no other purpose than to make the public API take the C++ string type. This is a primary location where std::experimental::string_view would be a far more appropriate type (a ponder-specific string_view for users not running bleeding-edge C++ implementations works too, of course).

Another example of unnecessary allocation from ClassBuilder::base<>:

std::string baseName = baseClass.name();

The .name() member function of Class returns a std::string const& yet in this line of code you're creating a new local value type and copying/allocating the string. This should at least be a local std::string const& baseName, assuming you don't remove use of std::string entirely.

File test/userproperty.hpp missing?

Is there supposed to be a file userproperty.hpp in the test folder?

It looks like this is included from test/userproperty.cpp to setup object MyClass.

I would like to see how this is done because I would like to have extra property classes, specifically wxDateTime like this:

struct ItemInfo
{
    int ID;
    std::string name;
    std::string fileName;
    wxDateTime created;
    etc.
};

It probably is already covered in the Composed* structures of the UserObject test, but it would be good to see anyway.

Benchmarks / performance

Have there been any benchmarks / performance tests written for Ponder (or CAMP)? I googled for some but cannot find anything.

I plan to do some basic testing, for example creating 10k objects with Class::construct and populating some properties but don't want to re-invent the wheel.

I see that the Catch test framework has some built-in timing capabilities, so maybe it is something to consider in the unit tests?

Catch returns 0 on failure

Catch unittesting appears to return 0 when test fail. Either need to add own main() or add logging and parse output.

Compile failed Ubuntu 14.04 - vector.erase

This library looks really great. Eager to try it.

To wit, I just did a git clone / cmake / make and I got the following (rather inscrutable) compiler error

 make
[  4%] Building CXX object CMakeFiles/ponder.dir/src/args.cpp.o
[  8%] Building CXX object CMakeFiles/ponder.dir/src/arrayproperty.cpp.o
[ 12%] Building CXX object CMakeFiles/ponder.dir/src/class.cpp.o
[ 16%] Building CXX object CMakeFiles/ponder.dir/src/classcast.cpp.o
[ 20%] Building CXX object CMakeFiles/ponder.dir/src/classmanager.cpp.o
[ 24%] Building CXX object CMakeFiles/ponder.dir/src/classvisitor.cpp.o
[ 28%] Building CXX object CMakeFiles/ponder.dir/src/enum.cpp.o
[ 32%] Building CXX object CMakeFiles/ponder.dir/src/enumbuilder.cpp.o
In file included from /home/david/ponder/include/ponder/enum.hpp:41:0,
                 from /home/david/ponder/src/enumbuilder.cpp:32:
/home/david/ponder/include/ponder/detail/dictionary.hpp: In instantiation of ‘void ponder::detail::Dictionary<KEY, VALUE, CMP>::erase(const KEY&) [with KEY = std::basic_string<char>; VALUE = long int; CMP = ponder::Enum::EnumCmp]’:
/home/david/ponder/include/ponder/detail/dictionary.hpp:110:18:   required from ‘void ponder::detail::Dictionary<KEY, VALUE, CMP>::insert(const KEY&, const VALUE&) [with KEY = std::basic_string<char>; VALUE = long int; CMP = ponder::Enum::EnumCmp]’
/home/david/ponder/src/enumbuilder.cpp:48:41:   required from here
/home/david/ponder/include/ponder/detail/dictionary.hpp:123:13: error: no matching function for call to ‘std::vector<std::pair<std::basic_string<char>, long int>, std::allocator<std::pair<std::basic_string<char>, long int> > >::erase(__gnu_cxx::__normal_iterator<const std::pair<std::basic_string<char>, long int>*, std::vector<std::pair<std::basic_string<char>, long int>, std::allocator<std::pair<std::basic_string<char>, long int> > > >&)’
             m_contents.erase(it);
             ^
/home/david/ponder/include/ponder/detail/dictionary.hpp:123:13: note: candidates are:
In file included from /usr/include/c++/4.8/vector:69:0,
                 from /usr/include/c++/4.8/bits/random.h:34,
                 from /usr/include/c++/4.8/random:50,
                 from /usr/include/c++/4.8/bits/stl_algo.h:65,
                 from /usr/include/c++/4.8/algorithm:62,
                 from /home/david/ponder/include/ponder/format.h:39,
                 from /home/david/ponder/include/ponder/detail/util.hpp:37,
                 from /home/david/ponder/include/ponder/error.hpp:38,
                 from /home/david/ponder/include/ponder/enumget.hpp:37,
                 from /home/david/ponder/include/ponder/enum.hpp:39,
                 from /home/david/ponder/src/enumbuilder.cpp:32:
/usr/include/c++/4.8/bits/vector.tcc:134:5: note: std::vector<_Tp, _Alloc>::iterator std::vector<_Tp, _Alloc>::erase(std::vector<_Tp, _Alloc>::iterator) [with _Tp = std::pair<std::basic_string<char>, long int>; _Alloc = std::allocator<std::pair<std::basic_string<char>, long int> >; std::vector<_Tp, _Alloc>::iterator = __gnu_cxx::__normal_iterator<std::pair<std::basic_string<char>, long int>*, std::vector<std::pair<std::basic_string<char>, long int>, std::allocator<std::pair<std::basic_string<char>, long int> > > >; typename std::_Vector_base<_Tp, _Alloc>::pointer = std::pair<std::basic_string<char>, long int>*]
     vector<_Tp, _Alloc>::
     ^
/usr/include/c++/4.8/bits/vector.tcc:134:5: note:   no known conversion for argument 1 from ‘__gnu_cxx::__normal_iterator<const std::pair<std::basic_string<char>, long int>*, std::vector<std::pair<std::basic_string<char>, long int>, std::allocator<std::pair<std::basic_string<char>, long int> > > >’ to ‘std::vector<std::pair<std::basic_string<char>, long int>, std::allocator<std::pair<std::basic_string<char>, long int> > >::iterator {aka __gnu_cxx::__normal_iterator<std::pair<std::basic_string<char>, long int>*, std::vector<std::pair<std::basic_string<char>, long int>, std::allocator<std::pair<std::basic_string<char>, long int> > > >}’
/usr/include/c++/4.8/bits/vector.tcc:146:5: note: std::vector<_Tp, _Alloc>::iterator std::vector<_Tp, _Alloc>::erase(std::vector<_Tp, _Alloc>::iterator, std::vector<_Tp, _Alloc>::iterator) [with _Tp = std::pair<std::basic_string<char>, long int>; _Alloc = std::allocator<std::pair<std::basic_string<char>, long int> >; std::vector<_Tp, _Alloc>::iterator = __gnu_cxx::__normal_iterator<std::pair<std::basic_string<char>, long int>*, std::vector<std::pair<std::basic_string<char>, long int>, std::allocator<std::pair<std::basic_string<char>, long int> > > >; typename std::_Vector_base<_Tp, _Alloc>::pointer = std::pair<std::basic_string<char>, long int>*]
     vector<_Tp, _Alloc>::
     ^
/usr/include/c++/4.8/bits/vector.tcc:146:5: note:   candidate expects 2 arguments, 1 provided
make[2]: *** [CMakeFiles/ponder.dir/src/enumbuilder.cpp.o] Error 1
make[1]: *** [CMakeFiles/ponder.dir/all] Error 2
make: *** [all] Error 2

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.