438: Extensible entry points in a class hierarchy (first version).

https://svn.lrde.epita.fr/svn/oln/trunk/static Index: ChangeLog from Roland Levillain <roland@lrde.epita.fr> Extensible entry points in a class hierarchy (first version). * stc/entry.hh: New. * tests/entry.cc: New test. * stc/Makefile.am (nobase_stc_HEADERS): Add entry.hh. * tests/Makefile.am (check_PROGRAMS): Add `entry'. (entry_SOURCES): New. stc/Makefile.am | 1 stc/entry.hh | 151 ++++++++++++++++++++++++++++++ tests/Makefile.am | 2 tests/entry.cc | 267 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 421 insertions(+) Index: tests/entry.cc --- tests/entry.cc (revision 0) +++ tests/entry.cc (revision 0) @@ -0,0 +1,267 @@ +/// Test the extensible entry points in a class hierarchy. + +#include <stc/entry.hh> + +#include <mlc/case.hh> +#include <mlc/assert.hh> +#include <mlc/is_a.hh> + +// FIXME: Fake. Remove this when mlc::abort_ is added to the project. +namespace mlc +{ + template <typename T> + class abort_ {}; +} + + +/*----------. +| Library. | +`----------*/ + +/* Simplified class diagram. + + + /top/ + ^ + | + ,------------+------------. + | | + /property1/ /property2/ + ^ ^ + | | + ,-----+-----. ,-----+-----. + | | | | + /property1a/ /property1b/ /property2a/ /property2b/ + | | | | + o o o o + o o + | | + stc::set_entry_node<C,1> stc::set_entry_node<C,1> + (first selector) (second selector) + ^ ^ + | | + + `------------+------------' + | + entry + ^ + | + foo + + + In fact, this is closer to this : + + /top/ + ^ + | + | + /property2/ + ^ + | + ,-----+-----. + | | + /property2a/ /property2b/ + | | + o o + o + | + stc::set_entry_node<foo,2> + (second selector) + ^ + | + /property1/ + ^ + | + ,-----+-----. + | | + /property1a/ /property1b/ + | | + o o + o + | + stc::set_entry_node<foo,1> + (first selector) + ^ + | + entry + ^ + | + foo + +*/ + +namespace my_lib +{ + // Virtual types. + // We don't use all the equipment of stc/properties.hh for + // simplicity purpose. + template <typename T> + struct vtypes + { + // Nothing. + }; + + /// Top of the hierachy. + struct top {}; + + /// Property 1. + /// \{ + struct property1 : virtual public top {}; + struct property1a : public property1 {}; + struct property1b : public property1 {}; + /// \} + + /// Property 2. + /// \{ + struct property2 : virtual public top {}; + struct property2a : public property2 {}; + struct property2b : public property2 {}; + /// \} +} + +mlc_case_equipment_for_namespace(my_lib); + + +// -------------------------- // +// Equipment for property 1. // +// -------------------------- // + +namespace my_lib +{ + // Switch on property 1. + /// \{ + struct prop1_tag; + + template <typename prop1_type> + struct case_<prop1_tag, prop1_type, 1> : + // Test. + public mlc::where_< mlc_is_a(prop1_type, int) > + { + // Super class if test succeed. + typedef property1a ret; + }; + + template <typename prop1_type> + struct case_<prop1_tag, prop1_type, 2> : + // Test. + public mlc::where_< mlc_is_a(prop1_type, long) > + { + // Super class if test succeed. + typedef property1b ret; + }; + + template <typename prop1_type> + struct default_case_<prop1_tag, prop1_type> + { + typedef mlc::abort_<prop1_tag> ret; + }; + /// \} + +} // End of namespace my_lib + +// FIXME: Maybe set/get_entry_node should be moved inside a macro? +namespace stc +{ + /// Property 1. + /// E is for Exact. + template <typename E> + struct set_entry_node<E, 1> : + public my_lib::switch_<my_lib::prop1_tag, + typename my_lib::vtypes<E>::prop1>::ret + { + }; +} + +// -------------------------- // +// Equipment for property 2. // +// -------------------------- // + +namespace my_lib +{ + // Switch on property 2. + /// \{ + struct prop2_tag; + + template <typename prop2_type> + struct case_<prop2_tag, prop2_type, 1> : + // Test. + public mlc::where_< mlc_is_a(prop2_type, float) > + { + // Super class if test succeed. + typedef property2a ret; + }; + + template <typename prop2_type> + struct case_<prop2_tag, prop2_type, 2> : + // Test. + public mlc::where_< mlc_is_a(prop2_type, double) > + { + // Super class if test succeed. + typedef property2b ret; + }; + + template <typename prop2_type> + struct default_case_<prop2_tag, prop2_type> + { + typedef mlc::abort_<prop2_tag> ret; + }; + /// \} + +} // End of namespace my_lib + +// FIXME: Maybe set/get_entry_node should be moved inside a macro? +namespace stc +{ + /// Property 2. + /// E is for Exact. + template <typename E> + struct set_entry_node<E, 2> : + public my_lib::switch_<my_lib::prop2_tag, + typename my_lib::vtypes<E>::prop2>::ret + { + }; +} + + +/*--------------. +| Client code. | +`--------------*/ + +// Fwd decl. +namespace client +{ + struct foo; +} + +// client::foo's vtypes. +namespace my_lib +{ + template <> + struct vtypes<client::foo> + { + typedef int prop1; + typedef double prop2; + }; +} + +namespace client +{ + struct foo : public stc::entry<foo> + { + }; +} + + +int +main () +{ + mlc::assert_<mlc_is_a_(client::foo, my_lib::top)>::check(); + + mlc::assert_<mlc_is_a_(client::foo, my_lib::property1)>::check(); + mlc::assert_<mlc_is_a_(client::foo, my_lib::property1a)>::check(); + mlc::assert_<mlc_is_not_a_(client::foo, my_lib::property1b)>::check(); + + mlc::assert_<mlc_is_a_(client::foo, my_lib::property2)>::check(); + mlc::assert_<mlc_is_not_a_(client::foo, my_lib::property2a)>::check(); + mlc::assert_<mlc_is_a_(client::foo, my_lib::property2b)>::check(); +} Index: tests/Makefile.am --- tests/Makefile.am (revision 437) +++ tests/Makefile.am (working copy) @@ -8,8 +8,10 @@ # when oln.m4 is available in the distribution. check_PROGRAMS = \ + entry \ properties properties_SOURCES = properties.cc +entry_SOURCES = entry.cc TESTS = $(check_PROGRAMS) Index: stc/entry.hh --- stc/entry.hh (revision 0) +++ stc/entry.hh (revision 0) @@ -0,0 +1,151 @@ +// Copyright (C) 2006 EPITA Research and Development Laboratory +// +// This file is part of the Olena Library. This library is free +// software; you can redistribute it and/or modify it under the terms +// of the GNU General Public License version 2 as published by the +// Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this library; see the file COPYING. If not, write to +// the Free Software Foundation, 59 Temple Place - Suite 330, Boston, +// MA 02111-1307, USA. +// +// As a special exception, you may use this file as part of a free +// software library without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to +// produce an executable, this file does not by itself cause the +// resulting executable to be covered by the GNU General Public +// License. This exception does not however invalidate any other +// reasons why the executable file might be covered by the GNU General +// Public License. + +#ifndef STATIC_ENTRY_HH +# define STATIC_ENTRY_HH + +/** \file stc/entry.hh + \brief Extensible entry points in a class hierarchy. + + The following simplified diagram shows a typical use of this + facility. + + First hierarchy Second hierarchy + + + /A/ /B/ + ^ ^ + | | + ,-----+-----. ,-----+-----. + | | | | + /A1/ /A2/ /B1/ /B2/ + | | | | + o o o o + o o + | | + stc::set_entry_node<C,1> stc::set_entry_node<C,2> + (first selector) (second selector) + ^ ^ + | | + + `------------+------------' + | + stc::entry + ^ + | + C + (a client class) + + + Actually, the inheritance tree is (almost) linear, and the real + code rather looks like this: + + /B/ + ^ + | + ,-----+-----. + | | + /B1/ /B2/ + | | + o o + o + | + stc::set_entry_node<C,2> + ^ + | + /A/ + ^ + | + ,-----+-----. + | | + /A1/ /A2/ + | | + o o + o + | + stc::set_entry_node<C,1> + ^ + | + stc::entry + ^ + | + C +*/ + +#include <mlc/if.hh> +#include <mlc/is_a.hh> +#include <mlc/comma.hh> + +namespace stc +{ + namespace internal + { + // End of the recurring inheritance. + struct none {}; + // No user class should derive from this class. + struct not_user_defined {}; + } + + /** \brief Entry point of a hierarchy. + To be possibly specialized by the user. + + This class is use as selector ``plugging'' itself (inheriting) + to (from) a bottom class in one of the multiple hierarchies. */ + template <typename exact, unsigned num> + struct set_entry_node : public internal::not_user_defined + { + // Nothing. + }; + + namespace internal + { + // Not to be defined by the user. + // FIXME: Use a lock? + template <typename exact, unsigned num> + struct get_entry_node : + public set_entry_node<exact, num>, + public mlc::if_< mlc_is_a( mlc_comma_1( set_entry_node<exact, num + 1> ), + internal::not_user_defined ), + internal::none, + internal::get_entry_node<exact, num + 1> >::ret + { + }; + } + + /** \brief Bottom entry point. + + This class must not to be specialized by the user. However, + this class is meant to be super class of classes below the + multiple hierarchies. */ + template <typename exact> + struct entry : public internal::get_entry_node<exact, 1> + { + }; + +} // end of namespace stc + +#endif // ! STATIC_ENTRY_HH Index: stc/Makefile.am --- stc/Makefile.am (revision 437) +++ stc/Makefile.am (working copy) @@ -2,4 +2,5 @@ stcdir = $(includedir)/stc nobase_stc_HEADERS = \ + entry.hh \ properties.hh
participants (1)
-
Roland Levillain