We've written a smart pointer class and have been using it with great success with the built-in Visual Studio STL implementation.
The problem is we've realized our performance bottlenecks are in the STL library in code ported from Linux (where the STL is significantly faster the way we're using it). So I'm trying to link in STLPort to see if it deals with our performance problems.
When using STLPort 5.2.1 however I get very strange build errors related to ambigous copy constructors. I've stripped it down to a 50 line C++ program
#include "stdafx.h"
#include <set>
using namespace std;
template<class T>
class CRefCountPtr
{
public:
CRefCountPtr(T* pT) : m_T(pT)
{
}
T** operator&()
{
return &m_T;
}
operator T*() const
{
return (T*)m_T;
}
bool operator< (T* pT) const
{
return m_T < pT;
}
T* m_T;
};
class Example
{
int example;
};
int _tmain(int argc, _TCHAR* argv[])
{
set< CRefCountPtr<Example> > blah;
Example ex;
blah.insert(&ex);
return 0;
}
The error I get back from VS2008SP1 is
stlport\stl\_tree.h(318) : error C2782: 'void stlp_std::_Copy_Construct(_Tp *,const _Tp &)' : template parameter '_Tp' is ambiguous
stlport\stl\_construct.h(130) : see declaration of 'stlp_std::_Copy_Construct'
could be 'CRefCountPtr<T>'
with
[
T=Example
]
or 'Example *'
.....
stlport_example.cpp(43) : see reference to class template instantiation 'stlp_std::set<_Key>' being compiled
with
[
_Key=CRefCountPtr<Example>
]
I'm kind of stuck at how to proceed here, anybody have any idea what's going on with this one?
It's actually your operator& that's causing ambiguity. In g++ the ambiguity is in destruct rather than construct but I assume it's a similar problem.
The compiler tries to take the address of your T to construct/destruct it, and gets back a T** instead of a CRefCountPtr<T>*, causing confusion.
I bet you could create your own specific allocator that knows how to deal with this (aka not a template) and get it to compile.
Probably better long term is to get rid of the operator& as it'll only cause confusion.
Related
See code below
#include <iostream>
#include <string>
namespace stringhelper
{
std::string to_string(int n) { return "0"; } // ignore wrong implementation. simplified for example purpose
}
using stringhelper::to_string;
class TestClass
{
public:
std::string to_string() const { return "TestClass:" + to_string(m_value); }
private:
int m_value;
};
int main()
{
TestClass tc;
std::cout << tc.to_string();
}
If TestClass does not implement function to_string(), within TestClass, it is able to resolve to_string(m_value) to stringhelper::to_string(int). However, the moment TestClass implements function to_string(), the compiler is unable to resolve to_string(int) to stringhelper::to_string.
Rather, it insists/resolves the function to TestClass::to_string and gave an error that the function TestClass::to_string does not take in 1 arguments.
Why is this so?
Environment:
Visual Studio 2008 Professional Edition 9.0.21022.8 RTM
Configuration: Win32
Windows 8
This behavior is not limited to Visual Studio 2008. If tested in modern Clang implementations you will see the same behaviour. As you may know, functions in derived classes which don't override functions in base classes but which have the same name will hide other functions of the same name in the base class.
The "problem" here is that you, by using the using statement introduces a function named to_string into a scope that is essentially a victim of the exact same thing as what happens in the above example, when looking at it from inside your class.
If the standard had you call member functions with this->foo() this would probably not have been an issue. But since function calls within a class are presumed to be part of the class and only if not found looked for in other scopes this becomes an issue.
Since you have an implementation in your class, that has priority and will be used. Since you want a version that takes an int as an argument, an overloaded version of your member function will be looked for and since it does not exist you get the error you see.
This is part of why using namespace can often introduce errors that might be unintuitive to understand. If you want to make sure you use the stringhelper::to_string implementation while you are in a class with a function that has the same name you have to be explicit.
This would work fine for instance, even if you keep your using statement.
#include <iostream>
#include <string>
namespace stringhelper
{
std::string to_string(int n) { return "0"; } // ignore wrong implementation. simplified for example purpose
}
using stringhelper::to_string;
class TestClass
{
public:
std::string to_string() const { return "TestClass:" + stringhelper::to_string(m_value); }
private:
int m_value;
};
int main()
{
TestClass tc;
std::cout << tc.to_string();
}
I am working on plugin system using Boost.dll
#include <boost/config.hpp>
#include <boost/dll/alias.hpp>
#include <memory>
class base {
public:
base(){};
~base(){};
template <typename T>
static std::shared_ptr<base> create() {
return std::make_shared<T>();
}
virtual void do1() = 0;
};
class derived : public base {
public:
derived(){};
~derived(){};
virtual void do1() override {}
};
BOOST_DLL_ALIAS(base::create<derived>, // <-- this function is exported with...
create_plugin // <-- ...this alias name
)
// auto a = base::create<derived>();
When I trying use factory method inside BOOST_DLL_ALIAS macro getting error like below.
cl /c /ID:\Download\Compressed\boost_1_67_0 a.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 19.12.25834 for x86
Copyright (C) Microsoft Corporation. All rights reserved.
a.cpp
a.cpp(27): error C2440: 'reinterpret_cast': cannot convert from 'overloaded-function' to 'intptr_t'
a.cpp(27): note: Context does not allow for disambiguation of overloaded function
Without BOOST_DLL_ALIAS macro when calling factory method (as shown in last commented line), it get compiled fine.
// this really does nothing:
template<class R, class...Args>
R(*to_fptr( R(*f)(Args...) ))(Args...) {
return f;
}
// except avoid the bug in MSVC:
BOOST_DLL_ALIAS(to_fptr(base::create<derived>),
create_plugin
)
that should fix it.
MSVC seems to break when you try to cast from the name of a template function to a intptr_t. This is a bug.
The above workaround changes it from directly dealing with the name of a template function to just a function pointer. By breaking the overload resolution and the cast to intptr_t apart, MSVC no longer chokes.
You could probably also do:
template<class T>
T identity( T t ) { return std::move(t); }
instead of to_fptr.
to_fptr is a function that takes a function pointer and returns it. The syntax to return a function pointer from a function is a bit ridiculous, which is why it is hard to read.
Inspired by this tangentially related answer, I found another solution:
const void * create_plugin =
reinterpret_cast<const void*>(
reinterpret_cast<std::intptr_t>(
reinterpret_cast<std::decay_t<decltype(&base::create<derived>)> >(
&base::create<derived>
)
)
);
https://godbolt.org/z/NVGz_0
This should do the trick:
BOOST_DLL_ALIAS(std::function<std::shared_ptr<base>(void)>(base::create<derived>), // <-- this function is exported with...
create_plugin // <-- ...this alias name
)
Indeed it seems MSVC has a bug here, as there should be no problem taking such an expression base::create<derived> and treating it directly as a function pointer. So instead, we "feed it a function pointer" ourselves using the magic of std::function.
I've chosen to use std::function as it involves no writing of any kind of wrapping code and relies directly on intent-delivering standard c++ code.
And of course don't forget to #include <functional> : )
I have a class Room and it holds a vector of shared_ptrs to Option objects like so:
private:
vector<shared_ptr<Option> > options;
But for some reason when I build, I get the following errors:
'shared_ptr' : unspecialized class template can't be used as a template argument for template parameter '_Ty', expected a real type
'std::tr1::shared_ptr' : use of class template requires template argument list
Strangely, I also have a vector of shared_ptrs, exact same syntax but there's no problem with that one.
There's also a bunch of places that bring up the error "'Option': undeclared identifier", which brings me to think it might be a problem with the Option class, but it seems to be fine. Here's the code for Option:
Option.h:
#pragma once
#include "Room.h"
#include <memory>
using namespace std;
class Option
{
protected:
int id;
char* text;
public:
Option(void);
Option(int, char*);
virtual ~Option(void);
char* getText();
int getID();
};
Option.cpp:
#include "Option.h"
#include "Room.h"
#include <memory>
using namespace std;
Option::Option(void)
{
}
Option::Option(int newID, char* newText){
id = newID;
text = newText;
}
Option::~Option(void)
{
}
char* Option::getText(){
return text;
}
int Option::getID(){
return id;
}
There is a bit of conjecture in this answer since you haven't posted the code for the Room class. I'm assuming this code
private:
vector<shared_ptr<Option> > options;
is in Room.h. Your Option.h file includes Room.h, hence the Room class gets declared before the Option class. So Option is an incomplete type when the Room class' destructor is compiled and the shared_ptr implementation tries to delete the Option object.
From the code above, I don't see why Option.h needs to include Room.h, in fact, it should be the other way around. If it does indeed need to include the file, you should be able to work around the problem by explicitly declaring Room::~Room() out-of-line in Room.cpp.
EDIT:
Turns out ~shared_ptr<T> does not require T to be a complete type. However, shared_ptr<T>( T* ) and shared_ptr<T>::reset( T* ) do, and the problem may be because some operation on the vector is invoking a call to one of these (more likely the former).
vector<shared_ptr<Option >>
You almost did that right :)
vector<shared_ptr<Option> >
It's the two > characters that, when touching, cause the strange errors you see. It is being interpreted as the >> operator.
BTW, thank you for posting your code exactly as it is rather than typing it back in and possibly hiding the mistake.
I think I've run into a (possible) VC6 (I know. It's what we use.) compiler error, but am open to the fact that I've just missed something dumb. Given the following code (It's just an example!):
#include <iostream>
// Class with template member function:
class SomeClass
{
public:
SomeClass() {};
template<class T>
T getItem()
{
return T();
};
};
// Dummy just used to recreate compiler error
class OtherClass
{
public:
OtherClass() {};
};
std::ostream& operator<<( std::ostream& oStr, const OtherClass& obj )
{
return oStr << "OtherClass!";
};
// Main illustrates the error:
int main(int argc, char* argv[])
{
SomeClass a;
OtherClass inst2 = a.getItem<OtherClass>(); // Error C2275 happens here!
std::cout << inst2 << std::endl;
return 0;
}
If I try to compile this code VC6, dies on a.getItem<OtherClass>() yielding:
Error C2275: 'OtherClass' : illegal use of this type as an expression.
Have I overlooked some trivial syntax issue? Am I breaking a rule?
This code compiles just fine under gcc 4.3.4. Is it yet another compliance issue with VC6?
Thanks!
Among many other things with the word template in it, VC6 couldn't deal with function templates where the template parameters aren't also function parameters. The common workaround was to add a dummy function parameter:
template<class T>
T getItem(T* /*dummy*/ = NULL)
{
return T();
} // note: no ; after function definitions
However, in general, VC6 is pretty lame and often chokes as soon as a TU contains the template keyword. I had to beat my head against it for several years (big code base compiled with several compilers/compiler versions; VC6 giving us an endless amount of trouble) and was very glad when I got rid of it in 2003.
This is likely to be a VC6 issue. Although VC6 compiles most basic templates correctly it is known to have many issues when you start to move towards the more advanced template uses. Member templates are an area where VC6 is known to be weak on conformance.
I believe that's another bug in VC6, you should really switch to a more up-to-date compiler.
I'm using Visual Studio 2008 with the Boost v1.42.0 library. If I use an enum as the template argument, I get a compile error when adding a value using push_back(). The compiler error is: 'T': is not a legal base class and the location of the error is move.hpp line 79.
#include <boost/interprocess/containers/vector.hpp>
class Test {
public:
enum Types {
Unknown = 0,
First = 1,
Second = 2,
Third = 3
};
typedef boost::container::vector<Types> TypesVector;
};
int main() {
Test::TypesVector o;
o.push_back(Test::First);
return 0;
}
If I use a std::vector instead it works. And if I resize the Boost version first and then set the values using the [] operator it also works.
Is there some way to make this work using push_back()?
Template backtrace of the error:
error C2516: 'T' : is not a legal base class
1> main.cpp(21) : see declaration of 'T'
1> main.cpp(21) : see reference to class template instantiation 'boost::interprocess::rv' being compiled
1> with
1> [
1> T=Test::Types
1> ]
I think you have find really a bug. I have posted to the Boost ML to track the issue and try to have more info.
For the moment the single workaround I see is to specialize the rv class as follows, but I'm not sure this will work on all the cases.
namespace boost {
namespace interprocess {
template <>
class rv<Test::Types>
{
Test::Types v;
rv();
~rv();
rv(rv const&);
void operator=(rv const&);
operator Test::Types() const {return v;}
};
}}
If this do not works you can try using int instead of enum.
enum {
Unknown = 0,
First = 1,
Second = 2,
Third = 3
};
typedef int Types;
Of course this has the drawback to loss the enum safety.
It sounds like Boost has some erroneous logic to determine whether to derive from T or not.
Naively, one might assume that any type besides a native type or pointer may be used as a base. However enums are neither bases nor primitive. Perhaps they failed to account for that.
It looks like Boost is incorrectly determining that enums are compatible with its rvalue-reference emulation.
The best way to solve this is to avoid use of enums in Boost Interprocess structures.
A hack like
namespace boost {
namespace interprocess { // get inside boost
template<>
class is_movable<Test::Types> // add custom specialization of is_movable
: public ::boost::mpl::bool_<false>
{};
}}
might patch things up. Untested.
Add this right after your #includes so it appears before the first use.