Commit f82c6cfa authored by Jan Möbius's avatar Jan Möbius

Merge branch 'master' into use_cmake_GTest_finder

parents 5c31bdde 8c6cb2b6
Pipeline #7700 passed with stage
in 143 minutes and 31 seconds
......@@ -38,13 +38,13 @@ IF "%BUILD_PLATFORM%" == "VS2013" (
set GENERATOR=Visual Studio 12%ARCH_VS%
set VS_PATH="C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\devenv.com"
IF "%ARCHITECTURE%" == "x64" (
set QT_INSTALL_PATH=E:\Qt\Qt5.3.1-vs2013-%STRING_ARCH%\5.3\msvc2013_64_opengl
set QT_BASE_CONFIG=-DQT5_INSTALL_PATH=E:\Qt\Qt5.3.1-vs2013-%STRING_ARCH%\5.3\msvc2013_64_opengl
set QT_INSTALL_PATH=E:\Qt\Qt5.7.0\5.7\msvc2013_64
set QT_BASE_CONFIG=-DQT5_INSTALL_PATH=E:\Qt\Qt5.7.0\5.7\msvc2013_64
)
IF "%ARCHITECTURE%" == "x32" (
set QT_INSTALL_PATH=E:\Qt\Qt5.3.1-vs2013-%STRING_ARCH%\5.3\msvc2013_opengl
set QT_BASE_CONFIG=-DQT5_INSTALL_PATH=E:\Qt\Qt5.3.1-vs2013-%STRING_ARCH%\5.3\msvc2013_opengl
set QT_INSTALL_PATH=E:\Qt\Qt5.7.0\5.7\msvc2013
set QT_BASE_CONFIG=-DQT5_INSTALL_PATH=E:\Qt\Qt5.7.0\5.7\msvc2013
)
)
......@@ -98,6 +98,7 @@ IF "%APPS%" == "ON" (
ECHO "============================================================="
ECHO "============================================================="
ECHO "Building with :"
whoami
ECHO "ARCHITECTURE : %ARCHITECTURE%"
ECHO "BUILD_PLATFORM : %BUILD_PLATFORM%"
ECHO "GTESTVERSION : %GTESTVERSION%"
......
cmake_minimum_required (VERSION 2.6)
cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR)
# Set and enforce C++-11 flags
......
......@@ -9,9 +9,25 @@
<tr valign=top><td><b>7.2</b> (?/?/?)</td><td>
<b>Core</b>
<ul>
<li>TriConnectivity: Added two functions split_edge and split_edge_copy to mask the PolyConnectivity functions of the same name (Prevents creation of valence 2 vertices on trimeshes)</li>
</ul>
<b>IO</b>
<ul>
<li>PLY Reader: Allowing the PLY reader to read custom face ( Thanks to morgan Leborgne for the patch)</li>
<li>PLY Reader: Fixed endless loop on unknown property list type</li>
</ul>
<b>Tools</b>
<ul>
<li>SmartTagger: Added the SmartTagger class to tag primitives (O(1) reset )</li>
</ul>
<b>Build System</b>
<ul>
<li>Rename the DEPRECATED macro into OM_DEPRECATED to prevent a macro clash with Intel MKL (Thanks to Morgan Leborgne for the patch)</li>
</ul>
......
......@@ -6,6 +6,7 @@
\li \subpage subdivider_docu
\li \subpage vdpm_docu
\li \subpage smoother_docu
\li \subpage smarttagger_docu
\li Miscellaneous
OpenMesh::StripifierT
......
......@@ -30,11 +30,13 @@ function (acg_print_configure_header _id _name)
acg_color_message ("${_escape}[40;37m* Package : ${_escape}[32m${_project} ${_escape}[37m *${_escape}[0m")
acg_color_message ("${_escape}[40;37m* Version : ${_escape}[32m${_version} ${_escape}[37m *${_escape}[0m")
# Just artistic. remove 2 spaces for release to make it look nicer ;-)
if (${CMAKE_BUILD_TYPE} MATCHES "Debug")
acg_color_message ("${_escape}[40;37m* Type : ${_escape}[32m${CMAKE_BUILD_TYPE} ${_escape}[37m *${_escape}[0m")
else()
acg_color_message ("${_escape}[40;37m* Type : ${_escape}[32m${CMAKE_BUILD_TYPE} ${_escape}[37m *${_escape}[0m")
if ( NOT WIN32 )
# Just artistic. remove 2 spaces for release to make it look nicer ;-)
if (${CMAKE_BUILD_TYPE} MATCHES "Debug")
acg_color_message ("${_escape}[40;37m* Type : ${_escape}[32m${CMAKE_BUILD_TYPE} ${_escape}[37m *${_escape}[0m")
else()
acg_color_message ("${_escape}[40;37m* Type : ${_escape}[32m${CMAKE_BUILD_TYPE} ${_escape}[37m *${_escape}[0m")
endif()
endif()
acg_color_message ("${_escape}[40;37m************************************************************${_escape}[0m")
......
......@@ -82,6 +82,7 @@
#include <OpenMesh/Core/IO/writer/STLWriter.hh>
#include <OpenMesh/Core/IO/writer/OMWriter.hh>
#include <OpenMesh/Core/IO/writer/PLYWriter.hh>
#include <OpenMesh/Core/IO/writer/VTKWriter.hh>
//=== NAMESPACES ==============================================================
......@@ -104,6 +105,7 @@ static BaseWriter* OFFWriterInstance = &OFFWriter();
static BaseWriter* STLWriterInstance = &STLWriter();
static BaseWriter* OMWriterInstance = &OMWriter();
static BaseWriter* PLYWriterInstance = &PLYWriter();
static BaseWriter* VTKWriterInstance = &VTKWriter();
//=============================================================================
......
......@@ -1201,185 +1201,187 @@ bool _PLYReader_::can_u_read(std::istream& _is) const {
_is >> keyword;
while (keyword != "end_header") {
if (keyword == "comment") {
std::getline(_is, line);
} else if (keyword == "element") {
_is >> elementName;
_is >> elementCount;
ElementInfo element;
element.name_ = elementName;
element.count_ = elementCount;
if (elementName == "vertex") {
vertexCount_ = elementCount;
element.element_ = VERTEX;
} else if (elementName == "face") {
faceCount_ = elementCount;
element.element_ = FACE;
} else {
omerr() << "PLY header unsupported element type: " << elementName << std::endl;
element.element_ = UNKNOWN;
}
elements_.push_back(element);
} else if (keyword == "property") {
std::string tmp1;
std::string tmp2;
// Read first keyword, as it might be a list
_is >> tmp1;
if (tmp1 == "list") {
_is >> listIndexType;
_is >> listEntryType;
_is >> propertyName;
ValueType indexType = Unsupported;
ValueType entryType = Unsupported;
if (listIndexType == "uint8") {
indexType = ValueTypeUINT8;
} else if (listIndexType == "uchar") {
indexType = ValueTypeUCHAR;
} else if (listIndexType == "int") {
indexType = ValueTypeINT;
} else {
omerr() << "Unsupported Index type for property list: " << listIndexType << std::endl;
continue;
}
entryType = get_property_type(listEntryType, listEntryType);
if (entryType == Unsupported) {
omerr() << "Unsupported Entry type for property list: " << listEntryType << std::endl;
}
if (keyword == "comment") {
std::getline(_is, line);
} else if (keyword == "element") {
_is >> elementName;
_is >> elementCount;
ElementInfo element;
element.name_ = elementName;
element.count_ = elementCount;
if (elementName == "vertex") {
vertexCount_ = elementCount;
element.element_ = VERTEX;
} else if (elementName == "face") {
faceCount_ = elementCount;
element.element_ = FACE;
} else {
omerr() << "PLY header unsupported element type: " << elementName << std::endl;
element.element_ = UNKNOWN;
}
PropertyInfo property(CUSTOM_PROP, entryType, propertyName);
property.listIndexType = indexType;
if (elementName == "face")
{
// special case for vertex indices
if (propertyName == "vertex_index" || propertyName == "vertex_indices")
{
property.property = VERTEX_INDICES;
if (!elements_.back().properties_.empty())
{
omerr() << "Custom face Properties defined, before 'vertex_indices' property was defined. They will be skipped" << std::endl;
elements_.back().properties_.clear();
}
} else {
options_ += Options::Custom;
}
elements_.push_back(element);
} else if (keyword == "property") {
std::string tmp1;
std::string tmp2;
// Read first keyword, as it might be a list
_is >> tmp1;
if (tmp1 == "list") {
_is >> listIndexType;
_is >> listEntryType;
_is >> propertyName;
ValueType indexType = Unsupported;
ValueType entryType = Unsupported;
if (listIndexType == "uint8") {
indexType = ValueTypeUINT8;
} else if (listIndexType == "uint16") {
indexType = ValueTypeUINT16;
} else if (listIndexType == "uchar") {
indexType = ValueTypeUCHAR;
} else if (listIndexType == "int") {
indexType = ValueTypeINT;
} else {
omerr() << "Unsupported Index type for property list: " << listIndexType << std::endl;
return false;
}
}
else
omerr() << "property " << propertyName << " belongs to unsupported element " << elementName << std::endl;
entryType = get_property_type(listEntryType, listEntryType);
elements_.back().properties_.push_back(property);
if (entryType == Unsupported) {
omerr() << "Unsupported Entry type for property list: " << listEntryType << std::endl;
}
} else {
// as this is not a list property, read second value of property
_is >> tmp2;
// Extract name and type of property
// As the order seems to be different in some files, autodetect it.
ValueType valueType = get_property_type(tmp1, tmp2);
propertyName = get_property_name(tmp1, tmp2);
PropertyInfo entry;
//special treatment for some vertex properties.
if (elementName == "vertex") {
if (propertyName == "x") {
entry = PropertyInfo(XCOORD, valueType);
vertexDimension_++;
} else if (propertyName == "y") {
entry = PropertyInfo(YCOORD, valueType);
vertexDimension_++;
} else if (propertyName == "z") {
entry = PropertyInfo(ZCOORD, valueType);
vertexDimension_++;
} else if (propertyName == "nx") {
entry = PropertyInfo(XNORM, valueType);
options_ += Options::VertexNormal;
} else if (propertyName == "ny") {
entry = PropertyInfo(YNORM, valueType);
options_ += Options::VertexNormal;
} else if (propertyName == "nz") {
entry = PropertyInfo(ZNORM, valueType);
options_ += Options::VertexNormal;
} else if (propertyName == "u" || propertyName == "s") {
entry = PropertyInfo(TEXX, valueType);
options_ += Options::VertexTexCoord;
} else if (propertyName == "v" || propertyName == "t") {
entry = PropertyInfo(TEXY, valueType);
options_ += Options::VertexTexCoord;
} else if (propertyName == "red") {
entry = PropertyInfo(COLORRED, valueType);
options_ += Options::VertexColor;
if (valueType == ValueTypeFLOAT || valueType == ValueTypeFLOAT32)
options_ += Options::ColorFloat;
} else if (propertyName == "green") {
entry = PropertyInfo(COLORGREEN, valueType);
options_ += Options::VertexColor;
if (valueType == ValueTypeFLOAT || valueType == ValueTypeFLOAT32)
options_ += Options::ColorFloat;
} else if (propertyName == "blue") {
entry = PropertyInfo(COLORBLUE, valueType);
options_ += Options::VertexColor;
if (valueType == ValueTypeFLOAT || valueType == ValueTypeFLOAT32)
options_ += Options::ColorFloat;
} else if (propertyName == "diffuse_red") {
entry = PropertyInfo(COLORRED, valueType);
options_ += Options::VertexColor;
if (valueType == ValueTypeFLOAT || valueType == ValueTypeFLOAT32)
options_ += Options::ColorFloat;
} else if (propertyName == "diffuse_green") {
entry = PropertyInfo(COLORGREEN, valueType);
options_ += Options::VertexColor;
if (valueType == ValueTypeFLOAT || valueType == ValueTypeFLOAT32)
options_ += Options::ColorFloat;
} else if (propertyName == "diffuse_blue") {
entry = PropertyInfo(COLORBLUE, valueType);
options_ += Options::VertexColor;
if (valueType == ValueTypeFLOAT || valueType == ValueTypeFLOAT32)
options_ += Options::ColorFloat;
} else if (propertyName == "alpha") {
entry = PropertyInfo(COLORALPHA, valueType);
options_ += Options::VertexColor;
options_ += Options::ColorAlpha;
if (valueType == ValueTypeFLOAT || valueType == ValueTypeFLOAT32)
options_ += Options::ColorFloat;
}
}
PropertyInfo property(CUSTOM_PROP, entryType, propertyName);
property.listIndexType = indexType;
//not a special property, load as custom
if (entry.value == Unsupported){
Property prop = CUSTOM_PROP;
options_ += Options::Custom;
entry = PropertyInfo(prop, valueType, propertyName);
}
if (elementName == "face")
{
// special case for vertex indices
if (propertyName == "vertex_index" || propertyName == "vertex_indices")
{
property.property = VERTEX_INDICES;
if (entry.property != UNSUPPORTED)
if (!elements_.back().properties_.empty())
{
elements_.back().properties_.push_back(entry);
omerr() << "Custom face Properties defined, before 'vertex_indices' property was defined. They will be skipped" << std::endl;
elements_.back().properties_.clear();
}
} else {
options_ += Options::Custom;
}
}
else
omerr() << "property " << propertyName << " belongs to unsupported element " << elementName << std::endl;
elements_.back().properties_.push_back(property);
} else {
omlog() << "Unsupported keyword : " << keyword << std::endl;
// as this is not a list property, read second value of property
_is >> tmp2;
// Extract name and type of property
// As the order seems to be different in some files, autodetect it.
ValueType valueType = get_property_type(tmp1, tmp2);
propertyName = get_property_name(tmp1, tmp2);
PropertyInfo entry;
//special treatment for some vertex properties.
if (elementName == "vertex") {
if (propertyName == "x") {
entry = PropertyInfo(XCOORD, valueType);
vertexDimension_++;
} else if (propertyName == "y") {
entry = PropertyInfo(YCOORD, valueType);
vertexDimension_++;
} else if (propertyName == "z") {
entry = PropertyInfo(ZCOORD, valueType);
vertexDimension_++;
} else if (propertyName == "nx") {
entry = PropertyInfo(XNORM, valueType);
options_ += Options::VertexNormal;
} else if (propertyName == "ny") {
entry = PropertyInfo(YNORM, valueType);
options_ += Options::VertexNormal;
} else if (propertyName == "nz") {
entry = PropertyInfo(ZNORM, valueType);
options_ += Options::VertexNormal;
} else if (propertyName == "u" || propertyName == "s") {
entry = PropertyInfo(TEXX, valueType);
options_ += Options::VertexTexCoord;
} else if (propertyName == "v" || propertyName == "t") {
entry = PropertyInfo(TEXY, valueType);
options_ += Options::VertexTexCoord;
} else if (propertyName == "red") {
entry = PropertyInfo(COLORRED, valueType);
options_ += Options::VertexColor;
if (valueType == ValueTypeFLOAT || valueType == ValueTypeFLOAT32)
options_ += Options::ColorFloat;
} else if (propertyName == "green") {
entry = PropertyInfo(COLORGREEN, valueType);
options_ += Options::VertexColor;
if (valueType == ValueTypeFLOAT || valueType == ValueTypeFLOAT32)
options_ += Options::ColorFloat;
} else if (propertyName == "blue") {
entry = PropertyInfo(COLORBLUE, valueType);
options_ += Options::VertexColor;
if (valueType == ValueTypeFLOAT || valueType == ValueTypeFLOAT32)
options_ += Options::ColorFloat;
} else if (propertyName == "diffuse_red") {
entry = PropertyInfo(COLORRED, valueType);
options_ += Options::VertexColor;
if (valueType == ValueTypeFLOAT || valueType == ValueTypeFLOAT32)
options_ += Options::ColorFloat;
} else if (propertyName == "diffuse_green") {
entry = PropertyInfo(COLORGREEN, valueType);
options_ += Options::VertexColor;
if (valueType == ValueTypeFLOAT || valueType == ValueTypeFLOAT32)
options_ += Options::ColorFloat;
} else if (propertyName == "diffuse_blue") {
entry = PropertyInfo(COLORBLUE, valueType);
options_ += Options::VertexColor;
if (valueType == ValueTypeFLOAT || valueType == ValueTypeFLOAT32)
options_ += Options::ColorFloat;
} else if (propertyName == "alpha") {
entry = PropertyInfo(COLORALPHA, valueType);
options_ += Options::VertexColor;
options_ += Options::ColorAlpha;
if (valueType == ValueTypeFLOAT || valueType == ValueTypeFLOAT32)
options_ += Options::ColorFloat;
}
}
//not a special property, load as custom
if (entry.value == Unsupported){
Property prop = CUSTOM_PROP;
options_ += Options::Custom;
entry = PropertyInfo(prop, valueType, propertyName);
}
if (entry.property != UNSUPPORTED)
{
elements_.back().properties_.push_back(entry);
}
}
streamPos = _is.tellg();
_is >> keyword;
if (_is.bad()) {
omerr() << "Error while reading PLY file header" << std::endl;
return false;
}
} else {
omlog() << "Unsupported keyword : " << keyword << std::endl;
}
streamPos = _is.tellg();
_is >> keyword;
if (_is.bad()) {
omerr() << "Error while reading PLY file header" << std::endl;
return false;
}
}
// As the binary data is directy after the end_header keyword
......
......@@ -470,7 +470,7 @@ class GenericCirculatorT_DEPRECATED : protected GenericCirculatorBaseT<Mesh> {
To be save, you can use the CW/CCW circulator definitions, which behave\
the same as the original ones, without the previously mentioned issues."
DEPRECATED( DECREMENT_DEPRECATED_WARNINGS_TEXT )
OM_DEPRECATED( DECREMENT_DEPRECATED_WARNINGS_TEXT )
#endif // NO_DECREMENT_DEPRECATED_WARNINGS
GenericCirculatorT_DEPRECATED& operator--() {
assert(this->mesh_);
......@@ -488,7 +488,7 @@ class GenericCirculatorT_DEPRECATED : protected GenericCirculatorBaseT<Mesh> {
/// Post-decrement
#ifndef NO_DECREMENT_DEPRECATED_WARNINGS
DEPRECATED( DECREMENT_DEPRECATED_WARNINGS_TEXT )
OM_DEPRECATED( DECREMENT_DEPRECATED_WARNINGS_TEXT )
#undef DECREMENT_DEPRECATED_WARNINGS_TEXT
#endif //NO_DECREMENT_DEPRECATED_WARNINGS
GenericCirculatorT_DEPRECATED operator--(int) {
......@@ -542,7 +542,7 @@ class GenericCirculatorT_DEPRECATED : protected GenericCirculatorBaseT<Mesh> {
return GenericCirculator_ValueHandleFns::is_valid(this->heh_,this->start_, this->lap_counter_);
}
DEPRECATED("current_halfedge_handle() is an implementation detail and should not be accessed from outside the iterator class.")
OM_DEPRECATED("current_halfedge_handle() is an implementation detail and should not be accessed from outside the iterator class.")
/**
* \deprecated
* current_halfedge_handle() is an implementation detail and should not
......@@ -552,7 +552,7 @@ class GenericCirculatorT_DEPRECATED : protected GenericCirculatorBaseT<Mesh> {
return this->heh_;
}
DEPRECATED("Do not use this error prone implicit cast. Compare to end-iterator or use is_valid(), instead.")
OM_DEPRECATED("Do not use this error prone implicit cast. Compare to end-iterator or use is_valid(), instead.")
/**
* \deprecated
* Do not use this error prone implicit cast. Compare to the
......@@ -567,7 +567,7 @@ class GenericCirculatorT_DEPRECATED : protected GenericCirculatorBaseT<Mesh> {
* \deprecated
* This function clutters your code. Use dereferencing operators -> and * instead.
*/
DEPRECATED("This function clutters your code. Use dereferencing operators -> and * instead.")
OM_DEPRECATED("This function clutters your code. Use dereferencing operators -> and * instead.")
value_type handle() const {
return **this;
}
......@@ -578,7 +578,7 @@ class GenericCirculatorT_DEPRECATED : protected GenericCirculatorBaseT<Mesh> {
* Implicit casts of iterators are unsafe. Use dereferencing operators
* -> and * instead.
*/
DEPRECATED("Implicit casts of iterators are unsafe. Use dereferencing operators -> and * instead.")
OM_DEPRECATED("Implicit casts of iterators are unsafe. Use dereferencing operators -> and * instead.")
operator value_type() const {
return **this;
}
......
......@@ -126,7 +126,7 @@ class GenericIteratorT {
* \deprecated
* This function clutters your code. Use dereferencing operators -> and * instead.
*/
DEPRECATED("This function clutters your code. Use dereferencing operators -> and * instead.")
OM_DEPRECATED("This function clutters your code. Use dereferencing operators -> and * instead.")
value_handle handle() const {
return hnd_;
}
......@@ -137,7 +137,7 @@ class GenericIteratorT {
* Implicit casts of iterators are unsafe. Use dereferencing operators
* -> and * instead.
*/
DEPRECATED("Implicit casts of iterators are unsafe. Use dereferencing operators -> and * instead.")
OM_DEPRECATED("Implicit casts of iterators are unsafe. Use dereferencing operators -> and * instead.")
operator value_handle() const {
return hnd_;
}
......
......@@ -162,27 +162,63 @@ public:
*
* \note The properties of the new edges, halfedges, and faces will be undefined!
*
* @param _eh Edge handle that should be splitted
* @param _eh Edge handle that should be split
* @param _vh Vertex handle that will be inserted at the edge
*/
void split(EdgeHandle _eh, VertexHandle _vh);
/** \brief Edge split (= 2-to-4 split)
*
*
* The function will introduce two new faces ( non-boundary case) or
* one additional face (if edge is boundary)
*
* \note The properties of the new edges, halfedges, and faces will be undefined!
*
* \note This is an override to prevent a direct call to PolyConnectivity split_edge,
* which would introduce a singular vertex with valence 2 which is not allowed
* on TriMeshes
*
* @param _eh Edge handle that should be split
* @param _vh Vertex handle that will be inserted at the edge
*/
inline void split_edge(EdgeHandle _eh, VertexHandle _vh) { TriConnectivity::split(_eh, _vh); }
/** \brief Edge split (= 2-to-4 split)
*
* The function will introduce two new faces ( non-boundary case) or
* one additional face (if edge is boundary)
*
* \note The properties of the new edges will be adjusted to the properties of the original edge
* \note The properties of the new faces and halfedges will be undefined
* \note The properties of the new edges and faces will be adjusted to the
* properties of the original edge and face
* \note The properties of the new halfedges will be undefined
*
* @param _eh Edge handle that should be splitted
* @param _eh Edge handle that should be split
* @param _vh Vertex handle that will be inserted at the edge
*/
void split_copy(EdgeHandle _eh, VertexHandle _vh);
/** \brief Edge split (= 2-to-4 split)
*
* The function will introduce two new faces ( non-boundary case) or
* one additional face (if edge is boundary)
*
* \note The properties of the new edges and faces will be adjusted to the
* properties of the original edge and face
* \note The properties of the new halfedges will be undefined
*
* \note This is an override to prevent a direct call to PolyConnectivity split_edge_copy,
* which would introduce a singular vertex with valence 2 which is not allowed
* on TriMeshes
*
* @param _eh Edge handle that should be split
* @param _vh Vertex handle that will be inserted at the edge
*/
inline void split_edge_copy(EdgeHandle _eh, VertexHandle _vh) { TriConnectivity::split_copy(_eh, _vh); }
/** \brief Face split (= 1-to-3) split, calls corresponding PolyMeshT function).
*
* @param _fh Face handle that should be splitted
* @param _fh Face handle that should be split
* @param _vh Vertex handle that will be inserted at the face
*/
inline void split(FaceHandle _fh, VertexHandle _vh)
......@@ -190,7 +226,7 @@ public:
/** \brief Face split (= 1-to-3) split, calls corresponding PolyMeshT function).
*
* @param _fh Face handle that should be splitted
* @param _fh Face handle that should be split
* @param _vh Vertex handle that will be inserted at the face
*/
inline void split_copy(FaceHandle _fh, VertexHandle _vh)
......
......@@ -83,17 +83,17 @@
#endif
#if defined(_MSC_VER)
# define DEPRECATED(msg) __declspec(deprecated(msg))
# define OM_DEPRECATED(msg) __declspec(deprecated(msg))
#elif defined(__GNUC__)
# if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) >= 40500 /* Test for GCC >= 4.5.0 */
# define DEPRECATED(msg) __attribute__ ((deprecated(msg)))
# define OM_DEPRECATED(msg) __attribute__ ((deprecated(msg)))
# else