The Artima Developer Community
Sponsored Link

Articles Forum
Enforcing Code Feature Requirements in C++

16 replies on 2 pages. Most recent reply: Jul 20, 2012 6:50 AM by Péter Szabados

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 16 replies on 2 pages [ « | 1 2 ]
Péter Szabados

Posts: 2
Nickname: petersohn
Registered: Jul, 2012

Re: Enforcing Code Feature Requirements in C++ Posted: Jul 5, 2012 4:01 AM
Reply to this message Reply
Advertisement
Interesting concept, but very inefficient. I really could not get over the exponential virtual inharitance tree. I was thinking whether there is a better version, and came up with one that uses implicit conversion rules instead of virtual inheritance. Here is the source code:

#ifndef CODEFEATURES_HPP_
#define CODEFEATURES_HPP_

#include <boost/mpl/vector.hpp>
#include <boost/mpl/placeholders.hpp>
#include <boost/mpl/find.hpp>
#include <boost/mpl/less.hpp>
#include <boost/mpl/copy.hpp>
#include <boost/mpl/fold.hpp>
#include <boost/mpl/sort.hpp>
#include <boost/mpl/remove_if.hpp>
#include <boost/mpl/empty_base.hpp>
#include <boost/mpl/transform.hpp>
#include <boost/mpl/contains.hpp>
#include <boost/mpl/and.hpp>
#include <boost/utility/enable_if.hpp>


namespace CodeFeatures {

using boost::mpl::_1;
using boost::mpl::_2;
using boost::mpl::_;

template<typename S>
struct Features
{
typedef S features_type;

template <typename OtherFeature>
Features(
const OtherFeature&,
typename boost::enable_if<
typename boost::mpl::fold<
typename OtherFeature::features_type,
boost::mpl::true_,
boost::mpl::and_<
boost::mpl::contains<S, _2>,
_1
>
>::type,
int
>::type = 0)
{}
};

}

#endif /* CODEFEATURES_HPP_ */


As it can be seen, it eliminates the need of AllCodeFeatures as well, so it can be generically applied to any feature set. It also works for larger amount of features. For example:


namespace mpl = boost::mpl;
namespace cf = CodeFeatures;

struct F1{};
struct F2{};
struct F3{};
struct F4{};
struct F5{};
struct F6{};
struct F7{};
struct F8{};
struct F9{};
struct F10{};
struct F11{};
struct F12{};

BOOST_STATIC_ASSERT((
boost::is_convertible<
cf::Features<mpl::vector<F4,F5,F6,F7,F8> >,
cf::Features<mpl::vector<F1,F2,F3,F4,F5,F6,F7,F8,F9,F10,F11,F12> >
>::value));

BOOST_STATIC_ASSERT((
boost::is_convertible<
cf::Features<mpl::vector<F1,F2,F3,F4,F5,F6,F7,F8,F9,F10,F11,F12> >,
cf::Features<mpl::vector<F1,F2,F3,F4,F5,F6,F7,F8,F9,F10,F11,F12> >
>::value));

BOOST_STATIC_ASSERT((
!boost::is_convertible<
cf::Features<mpl::vector<F1,F2,F3,F4,F5,F6,F7,F8,F9,F10,F11,F12> >,
cf::Features<mpl::vector<F1,F2,F3,F4,F5,F6,F8,F9,F10,F11,F12> >
>::value));

Péter Szabados

Posts: 2
Nickname: petersohn
Registered: Jul, 2012

Re: Enforcing Code Feature Requirements in C++ Posted: Jul 20, 2012 6:50 AM
Reply to this message Reply
I was thinking further about my idea, and realized the problem with this. It works well for basic purposes: to enforce certain functions to have a set of features. However, it doesn't work for automatic overload resolution. For example, if we have a function with an overload that is thread safe, and another that is both thread safe and exception safe, and we call it with a requirement of being only thread safe, we will get an error of ambiguous overload.

The original solution uses virtual inheritance, which provides a built-in overload resolution choosing the closest possible match. With implicit conversion, we can't use this algorithm. We have to choose the overload "manually" via enable_if. I built a forwarding of enable_if that computes this without the need to write large expressions for every overload. The disadvantage is that we need to know all the overloads in advance. Here is how it works.


namespace mpl = boost::mpl;
namespace cf = CodeFeatures;

typedef cf::Features<mpl::vector<ThreadSafe>> TSafe;
typedef cf::Features<mpl::vector<ThreadSafe, ExceptionSafe>> TESafe;

typedef mpl::vector<TSafe, TESafe> CalleeFeaturesList;

template <class Features>
typename cf::SelectFeatures<Features, TSafe, CalleeFeaturesList>::type
callee()
{
std::cout << "TSafe" << std::endl;
}


template <class Features>
typename cf::SelectFeatures<Features, TESafe, CalleeFeaturesList>::type
callee()
{
std::cout << "TESafe" << std::endl;
}


This version has advantages over the original implementation as well. First, as it uses templates and decides which overload to use with the metafunction on the top, we don't even need to instantiate the feature variables. Second, the template makes these functions pass its requirement transitively. For example, we have a caller function like this:


template <class Features>
typename cf::SelectFeatures<Features, TESafe, mpl::vector<TESafe>>::type
caller()
{
callee<Features>();
}


This function provides both thread safety and exception safety to its callers, and has no overloads. However, if we only need thread safety, it will call the TSafe version of callee, providing a more efficient program without needing to use overloads over the whole call hierarchy.

Of course, if we don't want to use templates, the original approach works just as fine:


typedef TESafe callerFeatures;
void callerNoTemplate(callerFeatures features)
{
calleeNoTemplate(features); // the original way
callee<callerFeatures>(); // of course we can't use the variable in the template parameter. We have to use the type name itself.
}




Here is the implementation. First, I introduced some new metafunctions because the lack of contains_if in Boost MPL.


template <typename Seq, typename Pred>
struct none_of: boost::is_same<
typename boost::mpl::find_if<Seq, Pred>::type,
typename boost::mpl::end<Seq>::type
> {};

template <typename Seq, typename Pred>
struct all_of: none_of<
Seq,
boost::mpl::not_<Pred>
> {};


With this, even the definition of Features becomes easier. Otherwise, it does the same as before (except that in my last post, I left out the default constructor).


template <typename S>
struct Features
{
typedef S features_type;

Features() {}

template <typename OtherFeature>
Features(
const OtherFeature&,
typename boost::enable_if<
typename detail::all_of<
typename OtherFeature::features_type,
boost::mpl::contains<S, _>
>::type,
int
>::type = 0)
{}
};


Now comes the implementation for SelectFeatures. In human words, it does the following: "Enable this overload if T is convertible to ThisType and not convertible to any types in AllTypes which ThisType is also not convertible to."


template <typename T, typename ThisType, typename AllTypes, typename ValueType = void>
struct SelectFeatures: boost::enable_if<
boost::mpl::and_<
boost::is_convertible<T, ThisType>,
detail::all_of<
AllTypes,
boost::mpl::or_<
boost::is_convertible<ThisType, _1>,
boost::mpl::not_<
boost::is_convertible<T, _1>
>
>
>
>,
ValueType
> {};

Flat View: This topic has 16 replies on 2 pages [ « | 1  2 ]
Topic: Defining "Done" in User Stories Previous Topic   Next Topic Topic: Coding with JRebel: Java Forever Changed

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use