Skip to content

Commit

Permalink
Fix for systems that don't allow definitions of stl containers with i…
Browse files Browse the repository at this point in the history
…ncomplete types

Definitions of stl containers with incomplete type definitions, e.g. by
means of forward declarations, results in undefined behavior according
to the c++ standard. This corrects the problem by using providing an
alternative implementation using smart pointers selectable at configure
time by the user.
  • Loading branch information
jensenb committed Mar 5, 2014
1 parent 1989c7d commit 0c6f37f
Show file tree
Hide file tree
Showing 9 changed files with 239 additions and 44 deletions.
24 changes: 22 additions & 2 deletions orocos_kdl/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,12 @@ CMAKE_MINIMUM_REQUIRED(VERSION 2.6)

PROJECT(orocos_kdl)

SET( KDL_VERSION 1.2.3 CACHE STRING "Version of Orocos KDL" )
SET( KDL_VERSION 1.2.3)
STRING( REGEX MATCHALL "[0-9]+" KDL_VERSIONS ${KDL_VERSION} )
LIST( GET KDL_VERSIONS 0 KDL_VERSION_MAJOR)
LIST( GET KDL_VERSIONS 1 KDL_VERSION_MINOR)
LIST( GET KDL_VERSIONS 2 KDL_VERSION_PATCH)

SET(KDL_VERSION_PATCH "${KDL_VERSION_PATCH}")
MESSAGE( "Orocos KDL version ${VERSION} (${KDL_VERSION_MAJOR}.${KDL_VERSION_MINOR}.${KDL_VERSION_PATCH})" )

SET( PROJ_SOURCE_DIR ${orocos_kdl_SOURCE_DIR} )
Expand Down Expand Up @@ -51,6 +50,27 @@ endif()
include_directories(${Eigen_INCLUDE_DIR})
SET(KDL_CFLAGS "${KDL_CFLAGS} -I${Eigen_INCLUDE_DIR}")

# Check the platform STL containers capabilities
include(config/CheckSTLContainers.cmake)
CHECK_STL_CONTAINERS()

# Set the default option appropriately
if(HAVE_STL_CONTAINER_INCOMPLETE_TYPES)
set(KDL_USE_NEW_TREE_INTERFACE_DEFAULT Off)
else(HAVE_STL_CONTAINER_INCOMPLETE_TYPES)
set(KDL_USE_NEW_TREE_INTERFACE_DEFAULT On)
endif(HAVE_STL_CONTAINER_INCOMPLETE_TYPES)

# Allow the user to select the Tree API version to use
set(KDL_USE_NEW_TREE_INTERFACE ${KDL_USE_NEW_TREE_INTERFACE_DEFAULT} CACHE BOOL "Use the new KDL Tree interface")

# The new interface requires the use of shared pointers
if(KDL_USE_NEW_TREE_INTERFACE)
# We need shared_ptr from boost since not all compilers are c++11 capable
find_package(Boost REQUIRED)
include_directories(${Boost_INCLUDE_DIRS})
endif(KDL_USE_NEW_TREE_INTERFACE)

INCLUDE (${PROJ_SOURCE_DIR}/config/DependentOption.cmake)

OPTION(ENABLE_TESTS OFF "Enable building of tests")
Expand Down
47 changes: 47 additions & 0 deletions orocos_kdl/config/CheckSTLContainers.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# - Macro to check whether the STL containers support incomplete types
# The issue at stake is discussed here in depth:
# http://www.drdobbs.com/the-standard-librarian-containers-of-inc/184403814
#
# Empirically libstdc++ and MSVC++ support containers of incomplete types
# whereas libc++ does not.
#
# The result is returned in HAVE_STL_CONTAINER_INCOMPLETE_TYPES
#
# Copyright 2014 Brian Jensen <Jensen dot J dot Brian at gmail dot com>
# Author: Brian Jensen <Jensen dot J dot Brian at gmail dot com>
#

macro(CHECK_STL_CONTAINERS)
INCLUDE(CheckCXXSourceCompiles)
SET(CMAKE_REQUIRED_FLAGS)
CHECK_CXX_SOURCE_COMPILES("
#include <string>
#include <map>
#include <vector>
class TreeElement;
typedef std::map<std::string, TreeElement> SegmentMap;
class TreeElement
{
TreeElement(const std::string& name): number(0) {}
public:
int number;
SegmentMap::const_iterator parent;
std::vector<SegmentMap::const_iterator> children;
static TreeElement Root(std::string& name)
{
return TreeElement(name);
}
};
int main()
{
return 0;
}
"
HAVE_STL_CONTAINER_INCOMPLETE_TYPES)

endmacro(CHECK_STL_CONTAINERS)
56 changes: 54 additions & 2 deletions orocos_kdl/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,52 @@ FILE( GLOB KDL_HPPS [^.]*.hpp [^.]*.inl)

FILE( GLOB UTIL_HPPS utilities/[^.]*.h utilities/[^.]*.hpp)

INCLUDE(CheckCXXSourceCompiles)
SET(CMAKE_REQUIRED_FLAGS)
CHECK_CXX_SOURCE_COMPILES("
#include <string>
#include <map>
#include <vector.hpp>
class TreeElement;
typedef std::map<std::string, TreeElement> SegmentMap;
class TreeElement
{
TreeElement(const std::string& name): number(0) {}
public:
int number;
SegmentMap::const_iterator parent;
std::vector<SegmentMap::const_iterator> children;
static TreeElement Root(std::string& name)
{
return TreeElement(name);
}
};
int main()
{
return 0;
}
"
HAVE_STL_CONTAINER_INCOMPLETE_TYPES)

if(HAVE_STL_CONTAINER_INCOMPLETE_TYPES)
SET(KDL_USE_NEW_TREE_INTERFACE_DEFAULT Off)
ELSE(HAVE_STL_CONTAINER_INCOMPLETE_TYPES)
SET(KDL_USE_NEW_TREE_INTERFACE_DEFAULT On)
ENDIF(HAVE_STL_CONTAINER_INCOMPLETE_TYPES)

SET(KDL_USE_NEW_TREE_INTERFACE ${KDL_USE_NEW_TREE_INTERFACE_DEFAULT} CACHE BOOL "Use the new KDL Tree interface")

#Sanity check, inform the user
IF(NOT HAVE_STL_CONTAINER_INCOMPLETE_TYPES AND NOT KDL_USE_NEW_TREE_INTERFACE)
MESSAGE(WARNING "You have chosen to use the current Tree Interface, but your platform doesn't support containers of "
"incomplete types, this configuration is likely invalid")
ENDIF()

#In Windows (Visual Studio) it is necessary to specify the postfix
#of the debug library name and no symbols are exported by kdl,
#so it is necessary to compile it as a static library
Expand All @@ -13,16 +59,22 @@ ELSE(MSVC)
SET(LIB_TYPE SHARED)
ENDIF(MSVC)

ADD_LIBRARY(orocos-kdl ${LIB_TYPE} ${KDL_SRCS})
CONFIGURE_FILE(config.h.in config.h @ONLY)

ADD_LIBRARY(orocos-kdl ${LIB_TYPE} ${KDL_SRCS} config.h.in)

SET_TARGET_PROPERTIES( orocos-kdl PROPERTIES
SOVERSION "${KDL_VERSION_MAJOR}.${KDL_VERSION_MINOR}"
VERSION "${KDL_VERSION}"
COMPILE_FLAGS "${CMAKE_CXX_FLAGS_ADD} ${KDL_CFLAGS}"
INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/lib"
PUBLIC_HEADER "${KDL_HPPS}"
PUBLIC_HEADER "${KDL_HPPS};${CMAKE_CURRENT_BINARY_DIR}/config.h"
)

# Needed so that the generated config.h can be used
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
TARGET_LINK_LIBRARIES(orocos-kdl ${Boost_LIBRARIES})

INSTALL(TARGETS orocos-kdl
EXPORT OrocosKDLTargets
ARCHIVE DESTINATION lib
Expand Down
37 changes: 37 additions & 0 deletions orocos_kdl/src/config.h.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright (C) 2014 Ruben Smits <ruben dot smits at mech dot kuleuven dot be>

// Version: 1.0
// Author: Brian Jensen <Jensen dot J dot Brian at gmail dot com>
// Maintainer: Ruben Smits <ruben dot smits at mech dot kuleuven dot be>
// URL: http://www.orocos.org/kdl

// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.

// 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
// Lesser General Public License for more details.

// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

#ifndef KDL_CONFIG_H
#define KDL_CONFIG_H

#define KDL_VERSION_MAJOR @KDL_VERSION_MAJOR@
#define KDL_VERSION_MINOR @KDL_VERSION_MINOR@
#define KDL_VERSION_PATCH @KDL_VERSION_PATCH@

#define KDL_VERSION (KDL_VERSION_MAJOR << 16) | (KDL_VERSION_MINOR << 8) | KDL_VERSION_PATCH

#define KDL_VERSION_STRING "@KDL_VERSION@"

//Set which version of the Tree Interface to use
#cmakedefine HAVE_STL_CONTAINER_INCOMPLETE_TYPES
#cmakedefine KDL_USE_NEW_TREE_INTERFACE

#endif //#define KDL_CONFIG_H
6 changes: 3 additions & 3 deletions orocos_kdl/src/kinfam_io.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ std::ostream& operator <<(std::ostream& os, const Tree& tree) {

std::ostream& operator <<(std::ostream& os, SegmentMap::const_iterator root) {
//os<<root->first<<": "<<root->second.segment<<"\n";
os << root->first<<"(q_nr: "<<root->second.q_nr<<")"<<"\n \t";
for (unsigned int i = 0; i < root->second.children.size(); i++) {
os <<(root->second.children[i])<<"\t";
os << root->first<<"(q_nr: "<< GetTreeElementQNr(root->second) << ")" << "\n \t";
for (unsigned int i = 0; i < GetTreeElementChildren(root->second).size(); i++) {
os << ( GetTreeElementChildren(root->second)[i] ) << "\t";
}
return os << "\n";
}
Expand Down
30 changes: 18 additions & 12 deletions orocos_kdl/src/tree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ Tree::Tree(const Tree& in) {
root_name = in.root_name;

segments.insert(make_pair(root_name, TreeElement::Root(root_name)));
this->addTree(in, root_name);
addTree(in, root_name);
}

Tree& Tree::operator=(const Tree& in) {
Expand All @@ -59,12 +59,18 @@ bool Tree::addSegment(const Segment& segment, const std::string& hook_name) {
pair<SegmentMap::iterator, bool> retval;
//insert new element
unsigned int q_nr = segment.getJoint().getType() != Joint::None ? nrOfJoints : 0;
retval = segments.insert(make_pair(segment.getName(), TreeElement(segment, parent, q_nr)));

#ifdef KDL_USE_NEW_TREE_INTERFACE
retval = segments.insert(make_pair(segment.getName(), TreeElementType( new TreeElement(segment, parent, q_nr))));
#else //#ifdef KDL_USE_NEW_TREE_INTERFACE
retval = segments.insert(make_pair(segment.getName(), TreeElementType(segment, parent, q_nr)));
#endif //#ifdef KDL_USE_NEW_TREE_INTERFACE

//check if insertion succeeded
if (!retval.second)
return false;
//add iterator to new element in parents children list
parent->second.children.push_back(retval.first);
GetTreeElementChildren(parent->second).push_back(retval.first);
//increase number of segments
nrOfSegments++;
//increase number of joints
Expand Down Expand Up @@ -92,10 +98,10 @@ bool Tree::addTreeRecursive(SegmentMap::const_iterator root, const std::string&
//get iterator for root-segment
SegmentMap::const_iterator child;
//try to add all of root's children
for (unsigned int i = 0; i < root->second.children.size(); i++) {
child = root->second.children[i];
for (unsigned int i = 0; i < GetTreeElementChildren(root->second).size(); i++) {
child = GetTreeElementChildren(root->second)[i];
//Try to add the child
if (this->addSegment(child->second.segment, hook_name)) {
if (this->addSegment(GetTreeElementSegment(child->second), hook_name)) {
//if child is added, add all the child's children
if (!(this->addTreeRecursive(child, child->first)))
//if it didn't work, return false
Expand All @@ -114,12 +120,12 @@ bool Tree::addTreeRecursive(SegmentMap::const_iterator root, const std::string&

// walk down from chain_root and chain_tip to the root of the tree
vector<SegmentMap::key_type> parents_chain_root, parents_chain_tip;
for (SegmentMap::const_iterator s=getSegment(chain_root); s!=segments.end(); s=s->second.parent){
for (SegmentMap::const_iterator s=getSegment(chain_root); s!=segments.end(); s = GetTreeElementParent(s->second)){
parents_chain_root.push_back(s->first);
if (s->first == root_name) break;
}
if (parents_chain_root.empty() || parents_chain_root.back() != root_name) return false;
for (SegmentMap::const_iterator s=getSegment(chain_tip); s!=segments.end(); s=s->second.parent){
for (SegmentMap::const_iterator s=getSegment(chain_tip); s!=segments.end(); s = GetTreeElementParent(s->second)){
parents_chain_tip.push_back(s->first);
if (s->first == root_name) break;
}
Expand All @@ -138,20 +144,20 @@ bool Tree::addTreeRecursive(SegmentMap::const_iterator root, const std::string&

// add the segments from the root to the common frame
for (unsigned int s=0; s<parents_chain_root.size()-1; s++){
Segment seg = getSegment(parents_chain_root[s])->second.segment;
Segment seg = GetTreeElementSegment(getSegment(parents_chain_root[s])->second);
Frame f_tip = seg.pose(0.0).Inverse();
Joint jnt = seg.getJoint();
if (jnt.getType() == Joint::RotX || jnt.getType() == Joint::RotY || jnt.getType() == Joint::RotZ || jnt.getType() == Joint::RotAxis)
jnt = Joint(jnt.getName(), f_tip*jnt.JointOrigin(), f_tip.M*(-jnt.JointAxis()), Joint::RotAxis);
else if (jnt.getType() == Joint::TransX || jnt.getType() == Joint::TransY || jnt.getType() == Joint::TransZ || jnt.getType() == Joint::TransAxis)
jnt = Joint(jnt.getName(),f_tip*jnt.JointOrigin(), f_tip.M*(-jnt.JointAxis()), Joint::TransAxis);
chain.addSegment(Segment(getSegment(parents_chain_root[s+1])->second.segment.getName(),
jnt, f_tip, getSegment(parents_chain_root[s+1])->second.segment.getInertia()));
chain.addSegment(Segment(GetTreeElementSegment(getSegment(parents_chain_root[s+1])->second).getName(),
jnt, f_tip, GetTreeElementSegment(getSegment(parents_chain_root[s+1])->second).getInertia()));
}

// add the segments from the common frame to the tip frame
for (int s=parents_chain_tip.size()-1; s>-1; s--){
chain.addSegment(getSegment(parents_chain_tip[s])->second.segment);
chain.addSegment(GetTreeElementSegment(getSegment(parents_chain_tip[s])->second));
}
return true;
}
Expand Down
62 changes: 48 additions & 14 deletions orocos_kdl/src/tree.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,38 +22,72 @@
#ifndef KDL_TREE_HPP
#define KDL_TREE_HPP

#include "config.h"

#include "segment.hpp"
#include "chain.hpp"

#include <string>
#include <map>

#ifdef KDL_USE_NEW_TREE_INTERFACE
#include <boost/shared_ptr.hpp>
#endif //#ifdef KDL_USE_NEW_TREE_INTERFACE

namespace KDL
{
//Forward declaration
class TreeElement;

#ifdef KDL_USE_NEW_TREE_INTERFACE
//We use smart pointers for managing tree nodes for now becuase
//c++11 and unique_ptr support is not ubiquitous
typedef boost::shared_ptr<TreeElement> TreeElementPtr;
typedef boost::shared_ptr<const TreeElement> TreeElementConstPtr;
typedef std::map<std::string, TreeElementPtr> SegmentMap;
typedef TreeElementPtr TreeElementType;

#define GetTreeElementChildren(tree_element) (tree_element)->children
#define GetTreeElementParent(tree_element) (tree_element)->parent
#define GetTreeElementQNr(tree_element) (tree_element)->q_nr
#define GetTreeElementSegment(tree_element) (tree_element)->segment

#else //#ifdef KDL_USE_NEW_TREE_INTERFACE
//Forward declaration
typedef std::map<std::string,TreeElement> SegmentMap;
typedef TreeElement TreeElementType;

#define GetTreeElementChildren(tree_element) (tree_element).children
#define GetTreeElementParent(tree_element) (tree_element).parent
#define GetTreeElementQNr(tree_element) (tree_element).q_nr
#define GetTreeElementSegment(tree_element) (tree_element).segment

#endif //#ifdef KDL_USE_NEW_TREE_INTERFACE

class TreeElement
{
private:
TreeElement(const std::string& name):segment(name), q_nr(0)
{};
public:
TreeElement(const Segment& segment_in,const SegmentMap::const_iterator& parent_in,unsigned int q_nr_in):
segment(segment_in),
q_nr(q_nr_in),
parent(parent_in)
{}

static TreeElementType Root(const std::string& root_name)
{
#ifdef KDL_USE_NEW_TREE_INTERFACE
return TreeElementType(new TreeElement(root_name));
#else //#define KDL_USE_NEW_TREE_INTERFACE
return TreeElementType(root_name);
#endif
}

Segment segment;
unsigned int q_nr;
SegmentMap::const_iterator parent;
std::vector<SegmentMap::const_iterator > children;
TreeElement(const Segment& segment_in,const SegmentMap::const_iterator& parent_in,unsigned int q_nr_in)
{
q_nr=q_nr_in;
segment=segment_in;
parent=parent_in;
};
static TreeElement Root(const std::string& root_name)
{
return TreeElement(root_name);
};

private:
TreeElement(const std::string& name):segment(name), q_nr(0) {}
};

/**
Expand Down
Loading

0 comments on commit 0c6f37f

Please sign in to comment.