Skip to content

Commit 7ec78ee

Browse files
committed
Implemented pure_virtual(...)
[SVN r19774]
1 parent 87c5e37 commit 7ec78ee

File tree

5 files changed

+221
-4
lines changed

5 files changed

+221
-4
lines changed
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright David Abrahams 2003. Permission to copy, use,
2+
// modify, sell and distribute this software is granted provided this
3+
// copyright notice appears in all copies. This software is provided
4+
// "as is" without express or implied warranty, and with no claim as
5+
// to its suitability for any purpose.
6+
#ifndef NULLARY_FUNCTION_ADAPTOR_DWA2003824_HPP
7+
# define NULLARY_FUNCTION_ADAPTOR_DWA2003824_HPP
8+
9+
namespace boost { namespace python { namespace detail {
10+
11+
// nullary_function_adaptor -- a class template which ignores its
12+
// arguments and calls a nullary function instead. Used for building
13+
// error-reporting functions, c.f. pure_virtual
14+
template <class NullaryFunction>
15+
struct nullary_function_adaptor
16+
{
17+
nullary_function_adaptor(NullaryFunction fn)
18+
: m_fn(fn)
19+
{}
20+
21+
void operator()() const { m_fn(); }
22+
23+
# define BOOST_PP_LOCAL_MACRO(i) \
24+
template <BOOST_PP_ENUM_PARAMS_Z(1, i, class A)> \
25+
void operator()( \
26+
BOOST_PP_ENUM_BINARY_PARAMS_Z(1, i, A, const& BOOST_PP_INTERCEPT) \
27+
) const \
28+
{ \
29+
m_fn(); \
30+
}
31+
32+
# define BOOST_PP_LOCAL_LIMITS (1, BOOST_PYTHON_MAX_ARITY)
33+
# include BOOST_PP_LOCAL_ITERATE()
34+
35+
private:
36+
NullaryFunction m_fn;
37+
};
38+
39+
}}} // namespace boost::python::detail
40+
41+
#endif // NULLARY_FUNCTION_ADAPTOR_DWA2003824_HPP
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
// Copyright David Abrahams 2003. Permission to copy, use,
2+
// modify, sell and distribute this software is granted provided this
3+
// copyright notice appears in all copies. This software is provided
4+
// "as is" without express or implied warranty, and with no claim as
5+
// to its suitability for any purpose.
6+
#ifndef PURE_VIRTUAL_DWA2003810_HPP
7+
# define PURE_VIRTUAL_DWA2003810_HPP
8+
9+
# include <boost/python/def_visitor.hpp>
10+
# include <boost/python/default_call_policies.hpp>
11+
# include <boost/python/arg_from_python.hpp>
12+
# include <boost/mpl/push_front.hpp>
13+
# include <boost/mpl/pop_front.hpp>
14+
15+
# include <boost/python/detail/nullary_function_adaptor.hpp>
16+
17+
namespace boost { namespace python {
18+
19+
namespace detail
20+
{
21+
//
22+
// { Helpers for pure_virtual_visitor, below.
23+
//
24+
25+
// Raises a Python RuntimeError reporting that a pure virtual
26+
// function was called.
27+
void BOOST_PYTHON_DECL pure_virtual_called();
28+
29+
// Replace the two front elements of S with T1 and T2
30+
template <class S, class T1, class T2>
31+
struct replace_front2
32+
{
33+
// Metafunction forwarding seemed to confound vc6
34+
typedef typename mpl::push_front<
35+
typename mpl::push_front<
36+
typename mpl::pop_front<
37+
typename mpl::pop_front<
38+
S
39+
>::type
40+
>::type
41+
, T2
42+
>::type
43+
, T1
44+
>::type type;
45+
};
46+
47+
// Given an MPL sequence representing a signature, returns a new MPL
48+
// sequence whose return type is replaced by void, and whose first
49+
// argument is replaced by C&.
50+
template <class C, class S>
51+
typename replace_front2<S,void,C&>::type
52+
error_signature(S BOOST_APPEND_EXPLICIT_TEMPLATE_TYPE(C))
53+
{
54+
typedef typename replace_front2<S,void,C&>::type r;
55+
return r();
56+
}
57+
58+
//
59+
// } Helpers for pure_virtual_visitor
60+
//
61+
62+
//
63+
// A def_visitor which defines a method as usual, then adds a
64+
// corresponding function which raises a "pure virtual called"
65+
// exception unless it's been overridden.
66+
//
67+
template <class PointerToMemberFunction>
68+
struct pure_virtual_visitor
69+
: def_visitor<pure_virtual_visitor<PointerToMemberFunction> >
70+
{
71+
pure_virtual_visitor(PointerToMemberFunction pmf)
72+
: m_pmf(pmf)
73+
{}
74+
75+
private:
76+
friend class def_visitor_access;
77+
78+
template <class C_, class Options>
79+
void visit(C_& c, char const* name, Options& options) const
80+
{
81+
// This should probably be a nicer error message
82+
BOOST_STATIC_ASSERT(!Options::has_default_implementation);
83+
84+
// Add the virtual function dispatcher
85+
c.def(
86+
name
87+
, m_pmf
88+
, options.doc()
89+
, options.keywords()
90+
, options.policies()
91+
);
92+
93+
typedef BOOST_DEDUCED_TYPENAME C_::select_holder::held_type held_t;
94+
95+
// Add the default implementation which raises the exception
96+
c.def(
97+
name
98+
, detail::make_function_aux(
99+
detail::nullary_function_adaptor<void(*)()>(pure_virtual_called)
100+
, default_call_policies()
101+
, args_from_python()
102+
, detail::error_signature<held_t>(detail::get_signature(m_pmf))
103+
)
104+
);
105+
}
106+
107+
private: // data members
108+
PointerToMemberFunction m_pmf;
109+
};
110+
}
111+
112+
//
113+
// Passed a pointer to member function, generates a def_visitor which
114+
// creates a method that only dispatches to Python if the function has
115+
// been overridden, either in C++ or in Python, raising a "pure
116+
// virtual called" exception otherwise.
117+
//
118+
template <class PointerToMemberFunction>
119+
detail::pure_virtual_visitor<PointerToMemberFunction>
120+
pure_virtual(PointerToMemberFunction pmf)
121+
{
122+
return detail::pure_virtual_visitor<PointerToMemberFunction>(pmf);
123+
}
124+
125+
}} // namespace boost::python
126+
127+
#endif // PURE_VIRTUAL_DWA2003810_HPP

src/object/function.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -627,7 +627,7 @@ handle<> function_handle_impl(py_function const& f)
627627
new function(f, 0, 0)));
628628
}
629629

630-
}
630+
} // namespace objects
631631

632632
namespace detail
633633
{
@@ -639,6 +639,11 @@ namespace detail
639639
f
640640
, keyword_range(&k,&k));
641641
}
642+
void BOOST_PYTHON_DECL pure_virtual_called()
643+
{
644+
PyErr_SetString(PyExc_RuntimeError, "Pure virtual function called");
645+
throw_error_already_set();
646+
}
642647
}
643648

644-
}} // namespace boost::python::objects
649+
}} // namespace boost::python

test/polymorphism.cpp

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <boost/python/manage_new_object.hpp>
1010
#include <boost/python/reference_existing_object.hpp>
1111
#include <boost/python/call_method.hpp>
12+
#include <boost/python/pure_virtual.hpp>
1213
#include <boost/python/def.hpp>
1314
#include <boost/utility.hpp>
1415

@@ -20,6 +21,27 @@ struct Callback
2021
PyObject* mSelf;
2122
};
2223

24+
struct P
25+
{
26+
virtual ~P(){}
27+
virtual std::string f() = 0;
28+
};
29+
30+
struct PCallback : P, Callback
31+
{
32+
PCallback (PyObject* self) : Callback(self) {}
33+
34+
std::string f()
35+
{
36+
return call_method<std::string>(mSelf, "f");
37+
}
38+
};
39+
40+
struct Q : P
41+
{
42+
std::string f() { return "Q::f()"; }
43+
};
44+
2345
struct A
2446
{
2547
virtual ~A(){}
@@ -44,7 +66,7 @@ struct ACallback : A, Callback
4466

4567
struct B : A
4668
{
47-
virtual std::string f() { return "B::f()"; }
69+
virtual std::string f() { return "B::f()"; }
4870
};
4971

5072
struct C : A
@@ -127,6 +149,13 @@ BOOST_PYTHON_MODULE_INIT(polymorphism_ext)
127149
def("factory", factory, return_value_policy<manage_new_object>());
128150

129151
def("call_f", call_f);
152+
153+
class_<P,boost::noncopyable,PCallback>("P")
154+
.def("f", pure_virtual(&P::f))
155+
;
156+
157+
class_<Q, bases<P> >("Q")
158+
;
130159
}
131160

132161
//#include "module_tail.cpp"

test/polymorphism.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,22 @@ def f(self):
4646
def test_wrapper_downcast(self):
4747
a = pass_a(D())
4848
self.failUnlessEqual('D::g()', a.g())
49-
49+
50+
def test_pure_virtual(self):
51+
p = P()
52+
self.assertRaises(RuntimeError, p.f)
53+
54+
q = Q()
55+
self.failUnlessEqual ('Q::f()', q.f())
56+
57+
class R(P):
58+
def f(self):
59+
return 'R.f'
60+
61+
r = R()
62+
self.failUnlessEqual ('R.f', r.f())
63+
64+
5065
if __name__ == "__main__":
5166

5267
# remove the option which upsets unittest

0 commit comments

Comments
 (0)