Saturday, June 27, 2009

c++ reflection

C++ has reflection. However it's currently very limited. The reflection I'm talking about is the new type_traits libraries. They are in boost, where in the TR1, and will be in in c++0x. So C++ supports without language changes a limited reflection system, often called static reflection or compile time reflection.

To make C++'s reflection compete with the likes of Java or C# we need three things.

1) Reflection needs to be more complete. We need to way to access method, and member information from classes.

2) Access to these facilities at runtime.

3) Reflection on attributes

The first is easy to design, though possibly difficult for compiler makers. Looking at gccxml I hope it's not too hard

The second is easy as well. The trick is to remember the C++ mantra
Don't pay for what you don't use.

Provide a runtime library that wraps the static reflection. The user is required to instantiate the library. Reflection of methods and members would need to return pointers to object it's describing.

The third is hard to plan for at this point as attributes have only just been added to c++.

Here are the new metamethods needed by the type_traits library complete static reflection. The new metamethods are similar to function_traits which currently exists in the type_traits library.
  • class_traits
  • union_traits
  • class_member_traits
  • class_method_traits
  • internal_class_traits
  • base_class_traits


I've posted many of these ideas already in comp.lang.c++.moderated

enum class visibilty_t
{
e_public,
e_protected,
e_private,
e_invisible // for private members of base classes.
};

typedef< typename Type >
struct class_traits
{
typedef Type type;

constexpr char[] name;
constexpr char[] mangled_name;
constexpr unsigned num_members;
constexpr unsigned num_methods;
constexpr unsigned num_classes;
constexpr unsigned num_unions;
constexpr unsigned num_bases;
};

template< typename Type >
struct union_traits
{
typedef Type type;

constexpr char[] name;
constexpr char[] mangle_name;
constexpr unsigned num_types;
typedef some_type memberN_type;
};

template< typename Type, unsigned Number >
struct class_member_traits
{
typedef Type parent_type;
constexpr unsigned number = Number;

constexpr char[] name;
constexpr char[] mangled_name;
constexpr visibility_t visibility;
constexpr bool is_inherited;
constexpr bool is_const;
typedef some_type type;
typedef some_type pointer_type;

constexpr pointer_type pointer = &some_class_member;
};

template< typename Type, unsigned Number >
struct class_method_traits
{
typedef Type parent_type;
constexpr unsigned number = Number;

constexpr char[] name;
constexpr char[] mangled_name;
constexpr char[] signiture;
constexpr visibility_t visibility;
constexpr bool is_inherited;
constexpr bool is_const;
constexpr bool is_virtual;
constexpr bool is_pure;
typedef some_type type;
typedef some_type pointer_type;

constexpr pointer_type pointer = &some_class_method;
};

template< typename Type, unsigned Number >
struct internal_class_traits
{
typedef Type parent_type;
constexpr unsigned number = Number;

constexpr visibility_t visibility;
typedef some_type type;
};

template< typename Type, unsigned Number >
struct internal_union_traits
{
typedef Type parent_type;
constexpr unsigned number = Number;

constexpr visibility_t visibility;
typedef some_type type;
};

template< typename Type, unsigned Number >
struct base_class_traits
{
typedef Type super_type;
constexpr unsigned number = Number;

constexpr visibility_t visibility;
constexpr bool is_virtual;
typedef some_type type;
};


The following a bit more complicated. namespaces don't have types. But it would be nice to enumerate all members of a namespace. My solution is a traits class that takes a member of the namespace. Types most internal namespace would be enumerated. eg namespace_traits would enumerate the contents of outer::inner and namespace_traits would enumerate the contents of outer. namespace_traits only enumerates complete types and any functions currently visible.


template< typename In_namespace >
struct namespace_traits
{
typedef In_namespace in_namespace_type;

constexpr unsigned num_classes;
typedef some_type classN_type;

constexpr unsigned num_unions;
typedef some_type unionN_type;

constexpr unsigned num_functions;
};

template< typename In_namespace, unsigned Function_number >
struct namespace_function_traits
{
typedef In_namespace in_namespace_type;
constexpr unsigned number = Function_number;

constexpr char[] name;
constexpr char[] signature;
constexpr char[] mangled_name;

typedef some_function_type type;
typedef type* pointer_type;

constexpr pointer_type pointer = &function_name;
};

Saturday, June 20, 2009

Subversion fail!!

The problem:
I need to refactor the directory structure of an active repository. This refactor could take some time

I am a big subversion fan. I've never really caught the distributed revision control bug. I think I like the idea of having an official location and copy of my repository built into the tool. I also understand repository permissions in subversion better. Distributed just seems too loose for my tastes.

I've recently however become a fan of Bazaar. It seems like they tried to make the idea of a central official copy and bake it into the tool. One of the unique features of bazaar is its true rename support. Git and mercurial just guess, but do a good job at it. Subversion just fails, completely.

Subversion just performs a copy, delete to mimic a copy. The problem with this approach is, file identity is lost. That means history can be broken, but svn does a good job at guessing here. It also means that updates can do weird things.

Enough rambling, here's the short of it. NEVER rename in svn. If you must rename, make sure no one has uncommited work that deals with the renamed file (this include every file under a directory rename), delete all branches.

People with uncommited work will end up in a weird situation where their file with changes isn't updated, instead svn creates a new file to reflect the renamed version with none of their modifications.

Branches are just hosed. Subversion doesn't lend itself to long term branching. Version 1.5 took a step in that direction however with merge history. None of that matters however if just one file is renamed that the branch has modified. THERE IS NO WAY to merge a branch to trunk or vis versa if a rename has occurred.

So the simple solution, avoid long term branches, perform renames only with much caution.

But what if you cannot stall development to refactor the directory structure? Wisdom would suggest, make a branch, perform the renames there, updating from main as you go. Once everything works, merge back to the trunk. That's what branches are for after all. As pointed out earlier that wont work because of the the abysmal state of rename in svn.

One defense: this is a free open source project, cut them some slack.

Ok, how long should we wait for them to fix this since it's such an obvious error? Let's be generous and give them 5 years. The development team realized they had made an error when implementing rename in 2002. Worse still the true rename is scheduled to be included in version 1.8. But even that means nothing! It was also scheduled to be fixed in 1.4, 1.5, 1.6, and 1.7. There is no 1.9 scheduled. So saying it will be fixed in 1.8 is just the official way of saying "not yet". We're looking at a 2010 fix at the earliest.

Subversion fail!


The solution:

All is not lost. Remember bazaar has true rename support. Even more importantly, it has a subversion backend plugin. bazaar-vcs.org/BzrForeignBranches/Subversion

Install Bazaar.
Install bzr-svn.
bzr branch
bzr push
perform refactoring, merging from trunk as needed.
bzr ci as often as possible
bzr checkout
bzr merge
bzr ci

Other advantages.
  • Long term branches
  • local checkins and repo checkins.
  • regular svn users can easily checkout the branch and use it. (Though I'd be wary of them doing any renames, who know what svn would screw up).