https://svn.lrde.epita.fr/svn/oln/trunk/static
Index: ChangeLog
from Roland Levillain <roland(a)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