MSVC friend function declaration bug - c++

Consider the following piece of code:
#include <cstddef>
template<size_t value> class dummy { };
class my_class
{
int m_member;
// Overload 1
template<size_t value>
friend void friend_func(dummy<value>*);
// Overload 2
template<size_t value>
friend void friend_func(int(*)[value]);
};
// Overload 1
template<size_t value>
void friend_func(dummy<value>*)
{
my_class instance;
instance.m_member = value;
}
// Overload 2
template<size_t value>
void friend_func(int(*)[value])
{
my_class instance;
instance.m_member = value;
}
int main(int argc, char* argv[])
{
dummy<5> d;
friend_func(&d); // call Overload 1
int arr[5];
friend_func(&arr); // call Overload 2 - error in MSVC!
return 0;
}
As you can see, the only difference between these two functions is that the second one takes a pointer to value ints instead of dummy<value>.
This code compiles just fine in GCC ($ gcc-4.7.2 test.cpp) and Clang (thanks WhozCraig), but throws the following error in MSVC (I tested 2012):
1>d:\path\to.cpp(32): error C2248: 'my_class::m_member' : cannot access private member declared in class 'my_class'
1> d:\path\to.cpp(8) : see declaration of 'my_class::m_member'
1> d:\path\to.cpp(7) : see declaration of 'my_class'
1> d:\path\to.cpp(40) : see reference to function template instantiation 'void friend_func<5>(int (*)[5])' being compiled
To me this looks like a bug. However, is there anyone who encountered such a behavior before? Is it really a bug, or maybe there's a particular reason for the error? Any quick workaround for this?
Edit: I've been able to find a proper workaround, see answer below.

It's definitely a bug: A template function parametrized on the size of an array cannot be declared as a friend of a class. It occurs when value is deduced as the size of the array for your friend template function. Here is a shortened version of your code that compiles fine. This example is the exact same code as your example except I specified the size of the array.
class my_class
{
int m_member;
template<size_t value>
friend void friend_func(int(*)[5]);
};
template<size_t value>
void friend_func(int(*)[5])
{
my_class instance;
instance.m_member = value;
}
int main()
{
int arr[5];
friend_func<5>(&arr);
}
One workaround it to pass the value as a second function argument:
template <typename T>
void friend_func(T, int value)
{
my_class instance;
instance.m_member = value;
}

Pretty sure it's a known issue with MSVS. Your specific issue is listed in the Portability Hints: Micrsoft Visual C++ on boost.org.
Look for Templates as Friends. I do not know the work around. However, I think you can make a class a friend. You may be able to use that as a work around.

I've figured a workaround that preserves the functionality yet does its job of preventing the error message. The idea is to use a proxy function and a proxy class to carry the pointer to the array and it's size. Here's the solution:
#include <cstddef>
// Workaround class for a bug in MSVC.
// https://connect.microsoft.com/VisualStudio/feedback/details/717749
// http://stackoverflow.com/questions/15149607
template<class element_type, size_t count>
class friend_declaration_bug_workaround
{
public:
typedef element_type(*ptr_type)[count];
private:
ptr_type m_arr;
public:
explicit friend_declaration_bug_workaround(ptr_type arr)
: m_arr(arr)
{
}
ptr_type value() const
{
return m_arr;
}
};
class my_class
{
int m_member;
friend void friend_func(int*);
template<size_t value>
friend void friend_func_workaround(friend_declaration_bug_workaround<int, value>);
};
template<size_t value>
void friend_func_workaround(friend_declaration_bug_workaround<int, value> workaround)
{
my_class instance;
instance.m_member = (*workaround.value())[0];
}
void friend_func(int* arr)
{
my_class instance;
instance.m_member = *arr;
}
template<size_t value>
void friend_func(int(*arr)[value])
{
friend_declaration_bug_workaround<int, value> workaround(arr);
return friend_func_workaround(workaround);
}
int main(int argc, char* argv[])
{
int value;
friend_func(&value); // call non-templated function
int arr[5];
friend_func(&arr); // call workarounded function
return 0;
}

Related

Make a safe Reader/Writer vector

Inspired by this code, I am trying to implement a Reader/Writer vector that can safely call push_back() concurrently by threads.
Once this class is in place, I might then create method erase() by calling std::swap(), which swaps the target item and the last item and then erase the last item in the collection. In this way, I assume that the performance should be fair because deleting an item in the middle of collection does not invoke moving all items following the target item in the collection.
Unfortunately, the following code:
#include <vector>
#include <boost/thread/shared_mutex.hpp> //shared_mutex
#include <memory> //shared_ptr
#include <utility> //swap()
template <class T>
class readers_writer_vector
{
std::shared_ptr<boost::shared_mutex> pm;
std::vector<T> data;
public:
readers_writer_vector() :
pm(new std::shared_ptr<boost::shared_mutex>){}
void push_back(const T& item){
boost::unique_lock<boost::shared_mutex> lock(*pm); //wrong design
data.push_back(item);
}
};
int main()
{
readers_writer_vector<int> db;
db.push_back(1);
return 0;
}
yields the following compilation errors:
/usr/include/c++/4.9/bits/shared_ptr_base.h:871:39: error: cannot convert ‘std::shared_ptr<boost::shared_mutex>*’ to ‘boost::shared_mutex*’ in initialization
: _M_ptr(__p), _M_refcount(__p)
// g++ -std=c++11 -Iboost -lboost t.cpp
How do I fix it? Please!
EDIT:
The implementation task is far more complicated than I thought. It didn't take too long before I encountered the problem #Danh had warned. Now I get these errors:
t.cpp:28:8: note: ‘i::i(const i&)’ is implicitly deleted because the default definition would be ill-formed:
struct i {
^
t.cpp:28:8: error: use of deleted function ‘readers_writer_vector<T>::readers_writer_vector(const readers_writer_vector<T>&) [with T = z]’
t.cpp:13:2: note: declared here
readers_writer_vector(readers_writer_vector const&) = delete;
with this version:
template <class T>
class readers_writer_vector
{
booster::shared_mutex m;
std::vector<T> data;
public:
readers_writer_vector() = default;
readers_writer_vector(readers_writer_vector const&) = delete;
void push_back(const T& item){
booster::unique_lock<booster::shared_mutex> lock(m);
data.push_back(item);
}
typename std::vector<T>::reference back(){
return data.back();
}
};
struct z {
int zipcode;
std::string address;
};
struct i {
int id;
readers_writer_vector<z> zipcodes;
};
int main()
{
readers_writer_vector<i> db;
db.push_back(i());
auto &ii=db.back();
ii.id=1;
ii.zipcodes.push_back(z());
auto &zz=ii.zipcodes.back();
zz.zipcode=11;
zz.address="aa";
return 0;
}
In addition to fixing the existing errors, I will have to implement iterators for readers_writer_vector to make this class useful.
I am pondering whether or not I should continue...
Because pm is std::shared_ptr<boost::shared_mutex> not std::shared_ptr<boost::shared_mutex>*. You can use this:
readers_writer_vector() :
pm(std::make_shared<boost::shared_mutex>()){}
Anyway, why do you need pointer/smart pointer? This is better fit:
template <class T>
class readers_writer_vector
{
boost::shared_mutex pm;
std::vector<T> data;
public:
void push_back(const T& item){
boost::unique_lock<boost::shared_mutex> lock(pm);
data.push_back(item);
}
};
You're initialising pm with the wrong type; you effectively have
std::shared_ptr<> pm = new std::shared_ptr<>;
You can't assign a shared pointer from a pointer to shared pointer.
Replace the initialiser with
pm(new boost::shared_mutex)
or make the mutex a member directly, rather than using shared pointer.

gcnew operator for generic type

I have the following simple class
generic<typename T> where T:IDbConnection ref class CDbConnection
{
private:
IDbConnection^m_db;
ConnectionState^ m_originalConnState;
public:
CDbConnection();
bool Connect(String ^ connStr);
bool Exists(int id);
auto GetAllData(String^ tableStr);
~CDbConnection();
!CDbConnection();
};
and here is my constructor
generic<typename T> CDbConnection<T>::CDbConnection()
{
m_db=gcnew T();
m_originalConnState=m_db->State;
}
But the compiler complains <1> the gcnew T() can't be used for generic type
<2> auto key in use is wrong as the function expects a trailing return type
Thank you for your reading and replies
I forgot this
where T:IDbConnection, gcnew()
which is exactly the same as C# generics
to get rid of the gcnew error as stated above.
In order to achieve genericity, you must change your class definition to
generic<typename T> where T:IDbConnection ref class CDbConnection
{
private:
T m_db;
ConnectionState^ m_originalConnState;
public:
CDbConnection();
bool Connect(String ^ connStr);
bool Exists(int id);
auto GetAllData(String^ tableStr);
~CDbConnection();
!CDbConnection();
};
As you are already constraining your T to be at least IDbConnection it can't be anything else.
Then your constructor
generic<typename T> CDbConnection<T>::CDbConnection()
{
m_originalConnState=m_db.State;
}
should work like you intended.
EDIT
It seems you cannot declare a reference to a generic. If you assign the object to the stack it will work.
See this entry.
// C3229.cpp
// compile with: /clr /c
generic <class T>
ref class C {
T^ t; // C3229
};
// OK
generic <class T>
ref class D {
T u;
};

Explicit template specialization

I hate to ask such a general question, but the following code is a exercise in explicit template specialization. I keep getting the error:
c:\users\***\documents\visual studio 2010\projects\template array\template array\array.h(49): error C2910: 'Array::{ctor}' : cannot be explicitly specialized
#ifndef ARRAY_H
#define ARRAY_H
template <typename t>`
class Array
{
public:
Array(int);
int getSize()
{
return size;
}
void setSize(int s)
{
size = s;
}
void setArray(int place, t value)
{
myArray[place] = value;
}
t getArray(int place)
{
return myArray[place];
}
private:
int size;
t *myArray;
};
template<typename t>
Array<t>::Array(int s=10)
{
setSize(s);
myArray = new t[getSize()];
}
template<>
class Array<float>
{
public:
Array();
};
template<>
Array<float>::Array()
{
cout<<"Error";
}
#endif
Thanks
The implementation of the specialization's constructor isn't a template! That is, you just want to write:
Array<float>::Array()
{
std::cout << "Error";
}
Actually, it seems that you want to restrict the use of your 'Array' class template to not be used with 'float' in which case you might want to only declare but not define you specialization to turn the run-time error into a compile-time error:
template <> class Array<float>;
Of course, there are many variations how you can prevent instantiation of classes. Creating a run-time error seems to be the worst option, however.

instantiating a free template function within a template class

I need to instantiate a free template function (FTF) within a template class (TC). The FTF takes as a template parameter one of the template parameters of the TC. The TC also holds generic pointers to these FTF's, and these functions are called through the pointers.
The step of taking a pointer to a FTF is not enough to instantiate it, and I receive linker errors from the GCC toolchain. MSDN illustrates FTF specification as so -- however my instantion of the FTF is dependant on a template parameter of my TC, and therefore the FTF instantiation cannot be placed in free scope.
Is this possible ? I am attaching some basic generated code, the issue is in the constructor of the class test_service, where I assign the pointer of a free function into a custom container. I get a linker error telling me the free function cannot be found (uninstantiated). I know that specifying a call to the template function in the class somewhere will produce a instantiation, however I am only going to be making a call via a pointer.
#include "rpc_common.h"
#include <boost/cstdint.hpp>
namespace rubble { namespace rpc {
struct test_service_dummy_tag{};
template<typename T>
class test_service_skel
{
public:
bool Init() {}
bool TearDown() {}
bool test_one(TestRequest,TestResponse){};
private:
};
template<typename T_IMPL>
bool test_service_test_one(T_IMPL & impl,ClientRequest & request)
{
return 0;
}
template<typename T_IMPL=test_service_skel<test_service_dummy_tag> >
class test_service
{
public:
test_service()
{
// uncomment the following two lines and a instantiation will occur.
// ClientRequest cr;
//test_service_test_one<T_IMPL>(m_impl,cr);
m_dispatch_table.SetEntry( Oid("test_one",0),(void *) & test_service_test_one<T_IMPL>);
}
bool Init() { return m_impl.Init(); };
bool TearDown() { return m_impl.TearDown(); };
private:
T_IMPL m_impl;
OidContainer<Oid,void *> m_dispatch_table;
};
} }
EDIT: self-contained minimal version
class test_skel
{
bool test_function()
{
return true;
}
};
template<typename T>
bool test_function()
{
}
template<typename T = test_skel>
class test
{
public:
test()
{
dispatch = (void *) & test_function<T>;
}
void * dispatch;
};
int main()
{
test<> t;
return 0;
}
There is no problem iff you don't use a void*, i.e.: http://www.ideone.com/eRgUG
However, if you insist on storing the pointer in a void*, then you need to take the address using a specific function pointer first and then cast - e.g.
bool (*temp)() = &test_function<T>;
dispatch = reinterpret_cast<void*>(temp); // YUCK
This gives the compiler enough context to generate the address for you.
Ahh - just saw DeadMG's answer, the function to generate the void* is neater...
Your self-contained example wouldn't compile for me with a strange error about overloaded functions, when there is no overloading going on, with MSVC. I did, however, manage to work around it.
class test_skel
{
bool test_function()
{
return true;
}
};
template<typename T> void* to_void_pointer(T t) {
return reinterpret_cast<void*>(t);
}
template<typename T>
bool test_function()
{
return true;
}
template<typename T = test_skel>
class test
{
public:
test()
{
dispatch = to_void_pointer(&test_function<T>);
}
void * dispatch;
};
int main()
{
test<> t;
return 0;
}
This compiles cleanly. I suspect that whatever behaviour you're seeing and I saw is a compiler error.

How to make this specialized function work

Here's the deal. I've looked on this forum and I didn't find the information I'm searching for or I'm probably not able to repeat it for my problem. I have a class Table which is generic and I have a class named MyString.
template <typename typeGen, int DIM>
class Table {
public:
TableauGenerique() : index_(0) { //On initialise courant à 0
}
void add(typeGen type);
private:
typeGen tableGen_[DIM];
int index_;
};
My problem is with the add function.
I sometimes have to do this in the main.cpp: (which works well)
Table <float,6> tabFloat;
tabFloat.add(1.6564);
and at one point, I need to do this which doesn't work because I need to specialize the add function to create an object of MyString, to pass it the string and then store the object in the array (tableGen) :
TableauGenerique <MyString,4> tabString;
So I tried this (after the class), without success.
template <typename typeGen, int DIM>
void Table<typeGen,DIM>::add(typeGen type){ //Which is the generic one for float or ints
if(index_ < DIM) {
tableGen_[courant_] = type;
index_++;
}
}
template <class typeGen, int DIM>
void Table<typeGen,DIM>::add<string>(typeGen type) { //(line 75) Which is the specific or specialized function for myString
MyString str(type);
if(index_ < DIM) {
tableGen_[courant_] = str;
index_++;
}
}
So, How can I make this work because it doesn't compile at all, saying: line75 : error: expected initializer before '<' token and in the main it says not matching function to call Table::add(const char[6]),
I hope everything is clear enough. Let me know if somethings is unclear.
Thank you very much for your help !
template <class typeGen, int DIM>
void Table<typeGen,DIM>::add<string>(typeGen type)
You're trying to specialize add() when in fact it is not a function template to begin with. How do you expect it to work?
You probably meant: (specialization of the class)
template <int DIM>
void Table<string,DIM>::add(string type)
But then this is allowed only if you specialize the class itself. Without specializing the class, the above code would give compilation error!
EDIT:
You can read these online tutorials:
Introduction to C++ Templates
14.5 — Class template specialization
Template Specialization and Partial Template Specialization
Explicit specialization (C++ only)
If you can control the code of the MyString class, you can provide constructors that act as implicit conversions from float to MyString. An example:
#include <string>
#include <sstream>
#include <iostream>
class MyString {
public:
MyString(float number) {
std::stringstream buffer;
buffer << number;
value = buffer.str();
}
void print() {
std::cout << value << std::endl;
}
private:
std::string value;
};
template <class T>
class Foo {
public:
void DoStuff(T item) {
item.print();
}
};
int main() {
Foo<MyString> foo;
foo.DoStuff(1.342); // implicitly converts float to MyString
return 0;
}
This way, you do not need any specialization of the add method. However, implicit conversions are tricky, and you have be careful not to invoke them accidentally, and they may create ambiguities.
EDIT: Upon a second thought, my suggestion below is basically equivalent to
Table<MyString,4> tabString;
tabString.add(MyString("whatever"));
and therefore excessive and/or does not solve the problem. Feel free to ignore :)
I would extend the class Table with a generic method to add something from which you can construct an object of the desired type:
template <typename typeGen, int DIM>
class Table {
public:
Table() : index_(0) {}
void add(typeGen type);
// The additional method
template<typename T> void add(const T& src);
private:
typeGen tableGen_[DIM];
int index_;
};
template<typename typeGen, int DIM>
template<typename T>
void Table<typeGen,DIM>::add(const T& src) {
if(index_ < DIM) {
tableGen_[courant_] = typeGen(src);
index_++;
}
}
Note construction of a temporary typeGen object before the assignment.
Assuming that MyString object can be constructed from a string literal, i.e. from const char*, you can then use it as following:
Table<MyString,4> tabString;
tabString.add("whatever");
or if the above assumption is wrong, the following should probably work (because you constructed a MyString instance from a string instance):
tabString.add(string("whatever"));