By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
449,412 Members | 993 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 449,412 IT Pros & Developers. It's quick & easy.

Circular Class Template Friendship

P: n/a
Hi,

I am converting my table and record classes into templates.
My issue is the syntax of declaring a friend class within
the template. I have searched the C++ FAQ Lite (web),
the C++ newsgroups, "Thinking In C++" to no avail.
Background
----------
My table is a collection of <integer, string> pairs, in
which the string is a fixed width that is specialized.
One specialization of the table may have a 32 length
string and another 64. The table is also a Singleton.

The record class is of the form <integer, string>.
The integer portion, the primary key, is hidden from
public usage, so that the class behaves like a string.
When the class is written as a field of a record, the
integer portion will be written out.

Part of the interface is for the table to be a friend
of the record. This allows the table to set the primary
key without giving access or knowledge of the primary
key to the general public.
The Code
---------
Here is my code:
// File Name_Id_Table.hpp

template <typename Record_Class,
const char * TABLE_NAME>
class Name_Id_Table
{
//...
public:
void load_from_table(Record_Class& rc)
{
rc.id = get_id_from_table();
rc.name = get_name_from_table();
}
};

// File Name_Id.hpp
#include <string>
using std::string

template <int MAX_STRING_WIDTH>
class Name_Id
{
// ...

/* The following line is what I need help with */
template <> friend class<Name_ID, ????> Name_Id_Table;

private:
int id;
string name;
};
// File main.cpp
#include "Name_Id_Table.hpp"
#include "Name_Id.hpp"

const char * TITLE_TABLE_NAME = "Titles";

/* Here is another issue I'm having problems with.
* I want to declare the types but am having syntax
* issues.
*/
typedef Name_Id<64> Title;
typedef Name_Id_Table<Title, TITLE_TABLE_NAME> Title_Table;

int main(void)
{
Title t;
Title_Table table;

table.load_from_table(t);

return EXIT_SUCCESS;
}

In the database, I will have three Name-ID tables:
Title, Author, Publisher. The string widths will
differ and they will have different table names.
Otherwise they have the same functionality. Each
table is a singleton; the Title table will not
contain author or publisher entries. Similarly
with Author and Publisher tables.
--
Thomas Matthews

C++ newsgroup welcome message:
http://www.slack.net/~shiva/welcome.txt
C++ Faq: http://www.parashift.com/c++-faq-lite
C Faq: http://www.eskimo.com/~scs/c-faq/top.html
alt.comp.lang.learn.c-c++ faq:
http://www.raos.demon.uk/acllc-c++/faq.html
Other sites:
http://www.josuttis.com -- C++ STL Library book
http://www.sgi.com/tech/stl -- Standard Template Library

Jul 22 '05 #1
Share this Question
Share on Google+
7 Replies


P: n/a
"Thomas Matthews" <Th*************************@sbcglobal.net> wrote...
I am converting my table and record classes into templates.
My issue is the syntax of declaring a friend class within
the template. I have searched the C++ FAQ Lite (web),
the C++ newsgroups, "Thinking In C++" to no avail.
Background
----------
My table is a collection of <integer, string> pairs, in
which the string is a fixed width that is specialized.
One specialization of the table may have a 32 length
string and another 64. The table is also a Singleton.

The record class is of the form <integer, string>.
The integer portion, the primary key, is hidden from
public usage, so that the class behaves like a string.
When the class is written as a field of a record, the
integer portion will be written out.

Part of the interface is for the table to be a friend
of the record. This allows the table to set the primary
key without giving access or knowledge of the primary
key to the general public.
The Code
---------
Here is my code:
// File Name_Id_Table.hpp

template <typename Record_Class,
const char * TABLE_NAME>
class Name_Id_Table
{
//...
public:
void load_from_table(Record_Class& rc)
{
rc.id = get_id_from_table();
rc.name = get_name_from_table();
}
};

// File Name_Id.hpp
#include <string>
using std::string
Ugh! Yuck!!! Never put a using declaration into a global scope in
a header. NEVER! There is no reason for it to be there. If you
are so inclined to save some typing and use 'string' instead of
'std::string' in your class definition, put the 'using' there, inside
that class definition. Hide your 'using's as deep as possible.

template <int MAX_STRING_WIDTH>
class Name_Id
{
// ...

/* The following line is what I need help with */
template <> friend class<Name_ID, ????> Name_Id_Table;
What are you trying to accomplish? All possible Name_Id_Table
instantiations with the same Name_ID should be friends or only
the one that has a particular TABLE_NAME?

At this point, it is still possible to have Name_Id_Table template
specialised on the same Name_Id class, but with different table
names. There is nothing in your code that prevents that.

So, do you want a particular fully specialised Name_Id_Table to
be a friend? Then you have to give the address of a constant
character here. Which one? You decide. The easiest solution
would be to give Name_Id an extra argument and pass it along to
the friend class declaration.

private:
int id;
string name;
};
// File main.cpp
#include "Name_Id_Table.hpp"
#include "Name_Id.hpp"

const char * TITLE_TABLE_NAME = "Titles";
No, that has to be 'extern'.

/* Here is another issue I'm having problems with.
* I want to declare the types but am having syntax
* issues.
WHAT syntax issues?
*/
typedef Name_Id<64> Title;
typedef Name_Id_Table<Title, TITLE_TABLE_NAME> Title_Table;

int main(void)
{
Title t;
Title_Table table;

table.load_from_table(t);

return EXIT_SUCCESS;
}

In the database, I will have three Name-ID tables:
Title, Author, Publisher. The string widths will
differ and they will have different table names.
You can create a templated "table of widths" and only worry
about passing the name to your 'Name_Id' template and to the
'Name_Id_Table' template:
---------------------------------------------------------
template<const char* name> struct Name_Id_Aux_Info { enum { MaxWidth }; };
template<typename T, const char* name> class Name_Id_Table;

template<const char* name> class Name_Id {
enum { MAX_STRING_WIDTH =
Name_Id_Aux_Info<name>::MaxWidth };

int foo() { return 42; } // private
friend class Name_Id_Table<Name_Id, name>;
};

template<typename T, const char* name> class Name_Id_Table
{
public:
int bar(T& t) { return t.foo(); }
};

extern const char TITLE[] = "Title";
extern const char AUTHOR[] = "Author";
extern const char PUBLISHER[] = "Publisher";

template<> struct Name_Id_Aux_Info<TITLE> { enum { MaxWidth = 32 }; };
template<> struct Name_Id_Aux_Info<AUTHOR> { enum { MaxWidth = 64 }; };
template<> struct Name_Id_Aux_Info<PUBLISHER> { enum { MaxWidth = 96 }; };

typedef Name_Id<TITLE> id_Title;
typedef Name_Id_Table<id_Title, TITLE> id_Title_Table;

int main()
{
id_Title idt;
id_Title_Table idtt;

idtt.bar(idt);

return 0;
}
Otherwise they have the same functionality. Each
table is a singleton; the Title table will not
contain author or publisher entries. Similarly
with Author and Publisher tables.


If you tie them by the name only, you have a chance, I believe.
Jul 22 '05 #2

P: n/a
Thomas Matthews wrote in news:41**************@sbcglobal.net in
comp.lang.c++:

Hi,

#include <cstdlib>
//^^^^^^^^^^^^^^^^ EXIT_SUCCESS
template <typename Record_Class,
const char * TABLE_NAME>
class Name_Id_Table
{
public:
void load_from_table(Record_Class& rc)
{
// [snipped]
}
};

// File Name_Id.hpp
#include <string>
using std::string;
// ^^^^^^missing ;
template <int MAX_STRING_WIDTH>
class Name_Id
{
// ...

/* The following line is what I need help with */ template < typename, const char * >
friend class Name_Id_Table;

/* ^^^^^^^^^^^^^^^^^^^^^^^^

friend-ship needs to be the class template or an explicit
specialization, partial-specialization's not allowed.
*/
private:
int id;
string name;
};

extern char const TITLE_TABLE_NAME[] = "Titles";
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

/* Here is another issue I'm having problems with.
* I want to declare the types but am having syntax
* issues.
See Above ^^^^^^^
*/
typedef Name_Id<64> Title;
typedef Name_Id_Table<Title, TITLE_TABLE_NAME> Title_Table;

int main(void)
{
Title t;
Title_Table table;

table.load_from_table(t);

return EXIT_SUCCESS;
}

BTW - Most people prefer lowercase or MixedCase for non-macro
identifiers, for example TABLE_NAME and TITLE_TABLE_NAME above.

HTH.

Rob.
--
http://www.victim-prime.dsl.pipex.com/
Jul 22 '05 #3

P: n/a
Victor Bazarov wrote in news:HzwMc.21062$eM2.20260@attbi_s51 in
comp.lang.c++:
// File Name_Id.hpp
#include <string>
using std::string
Ugh! Yuck!!! Never put a using declaration into a global scope in
a header. NEVER! There is no reason for it to be there.


Can't disagree with that.
If you
are so inclined to save some typing and use 'string' instead of
'std::string' in your class definition, put the 'using' there, inside
that class definition. Hide your 'using's as deep as possible.


#include <string>

struct x
{
using std::string;
};

test.cpp(5) : error C2886: 'std::string' : symbol cannot be used in
a member using-declaration

A typedef does work though:

#include <string>

struct x
{
typedef std::string string;
};

Rob.
--
http://www.victim-prime.dsl.pipex.com/
Jul 22 '05 #4

P: n/a
Victor Bazarov wrote:
"Thomas Matthews" <Th*************************@sbcglobal.net> wrote...
// File Name_Id.hpp
#include <string>
using std::string

Ugh! Yuck!!! Never put a using declaration into a global scope in
a header. NEVER! There is no reason for it to be there. If you
are so inclined to save some typing and use 'string' instead of
'std::string' in your class definition, put the 'using' there, inside
that class definition. Hide your 'using's as deep as possible.

I've decided to move it out of the class because it doesn't
compile when inside the class. See Rob's response to your article.

[snip]
In the database, I will have three Name-ID tables:
Title, Author, Publisher. The string widths will
differ and they will have different table names.

You can create a templated "table of widths" and only worry
about passing the name to your 'Name_Id' template and to the
'Name_Id_Table' template:
---------------------------------------------------------
template<const char* name> struct Name_Id_Aux_Info { enum { MaxWidth }; };
template<typename T, const char* name> class Name_Id_Table;

template<const char* name> class Name_Id {
enum { MAX_STRING_WIDTH =
Name_Id_Aux_Info<name>::MaxWidth };

int foo() { return 42; } // private
friend class Name_Id_Table<Name_Id, name>;

At this point, my compiler, Borland Builder 6.0, gives me this
error:
E2299 Cannot generate template specialization from
'Name_Id_Table<T, name>'

Although Gnu G++ 3.3.1 compiles without any warnings or errors.
};

template<typename T, const char* name> class Name_Id_Table
{
public:
int bar(T& t) { return t.foo(); }
};

extern const char TITLE[] = "Title";
extern const char AUTHOR[] = "Author";
extern const char PUBLISHER[] = "Publisher";

template<> struct Name_Id_Aux_Info<TITLE> { enum { MaxWidth = 32 }; };
template<> struct Name_Id_Aux_Info<AUTHOR> { enum { MaxWidth = 64 }; };
template<> struct Name_Id_Aux_Info<PUBLISHER> { enum { MaxWidth = 96 }; };

typedef Name_Id<TITLE> id_Title;
typedef Name_Id_Table<id_Title, TITLE> id_Title_Table;

int main()
{
id_Title idt;
id_Title_Table idtt;

idtt.bar(idt);

return 0;
}


Is Gnu correct in this case or Borland?
{Generates same error with BCC32 version 5.6}
--
Thomas Matthews

C++ newsgroup welcome message:
http://www.slack.net/~shiva/welcome.txt
C++ Faq: http://www.parashift.com/c++-faq-lite
C Faq: http://www.eskimo.com/~scs/c-faq/top.html
alt.comp.lang.learn.c-c++ faq:
http://www.raos.demon.uk/acllc-c++/faq.html
Other sites:
http://www.josuttis.com -- C++ STL Library book

Jul 22 '05 #5

P: n/a
Thomas Matthews wrote:
Victor Bazarov wrote:
You can create a templated "table of widths" and only worry
about passing the name to your 'Name_Id' template and to the
'Name_Id_Table' template:
---------------------------------------------------------
template<const char* name> struct Name_Id_Aux_Info { enum { MaxWidth
}; };
template<typename T, const char* name> class Name_Id_Table;

template<const char* name> class Name_Id {
enum { MAX_STRING_WIDTH =
Name_Id_Aux_Info<name>::MaxWidth };

int foo() { return 42; } // private
friend class Name_Id_Table<Name_Id, name>;


At this point, my compiler, Borland Builder 6.0, gives me this
error:
E2299 Cannot generate template specialization from
'Name_Id_Table<T, name>'

Although Gnu G++ 3.3.1 compiles without any warnings or errors.
};

template<typename T, const char* name> class Name_Id_Table
{
public:
int bar(T& t) { return t.foo(); }
};

extern const char TITLE[] = "Title";
extern const char AUTHOR[] = "Author";
extern const char PUBLISHER[] = "Publisher";

template<> struct Name_Id_Aux_Info<TITLE> { enum { MaxWidth = 32 }; };
template<> struct Name_Id_Aux_Info<AUTHOR> { enum { MaxWidth = 64 }; };
template<> struct Name_Id_Aux_Info<PUBLISHER> { enum { MaxWidth = 96
}; };

typedef Name_Id<TITLE> id_Title;
typedef Name_Id_Table<id_Title, TITLE> id_Title_Table;

int main()
{
id_Title idt;
id_Title_Table idtt;

idtt.bar(idt);

return 0;
}

Is Gnu correct in this case or Borland?
{Generates same error with BCC32 version 5.6}


The code I posted compiles fine with Comeau online and with VC++ v7.1
which kind of suggests that the message Borland compiler emits is not
an indication of the error in the code but rather the admission of
BCBuilder's impotency.

Victor
Jul 22 '05 #6

P: n/a
Victor Bazarov wrote:
Thomas Matthews wrote:
Victor Bazarov wrote:
You can create a templated "table of widths" and only worry
about passing the name to your 'Name_Id' template and to the
'Name_Id_Table' template:
---------------------------------------------------------
template<const char* name> struct Name_Id_Aux_Info { enum { MaxWidth
}; };
template<typename T, const char* name> class Name_Id_Table;

template<const char* name> class Name_Id {
enum { MAX_STRING_WIDTH =
Name_Id_Aux_Info<name>::MaxWidth };

int foo() { return 42; } // private
friend class Name_Id_Table<Name_Id, name>;

At this point, my compiler, Borland Builder 6.0, gives me this
error:
E2299 Cannot generate template specialization from
'Name_Id_Table<T, name>'

Although Gnu G++ 3.3.1 compiles without any warnings or errors.
};

template<typename T, const char* name> class Name_Id_Table
{
public:
int bar(T& t) { return t.foo(); }
};

extern const char TITLE[] = "Title";
extern const char AUTHOR[] = "Author";
extern const char PUBLISHER[] = "Publisher";

template<> struct Name_Id_Aux_Info<TITLE> { enum { MaxWidth = 32 }; };
template<> struct Name_Id_Aux_Info<AUTHOR> { enum { MaxWidth = 64 }; };
template<> struct Name_Id_Aux_Info<PUBLISHER> { enum { MaxWidth = 96
}; };

typedef Name_Id<TITLE> id_Title;
typedef Name_Id_Table<id_Title, TITLE> id_Title_Table;

int main()
{
id_Title idt;
id_Title_Table idtt;

idtt.bar(idt);

return 0;
}


Is Gnu correct in this case or Borland?
{Generates same error with BCC32 version 5.6}

The code I posted compiles fine with Comeau online and with VC++ v7.1
which kind of suggests that the message Borland compiler emits is not
an indication of the error in the code but rather the admission of
BCBuilder's impotency.

Victor


I posted your program in a Borland newsgroup and they suggested
the following modification:

template<const char* name> class Name_Id {
enum { MAX_STRING_WIDTH =
Name_Id_Aux_Info<name>::MaxWidth };

int foo() { return 42; } // private
friend class Name_Id_Table<Name_Id<name>, name>;
/* ^^^^^^ */

This makes sense, but I don't know why the other compilers
don't require it.

When I make the change, the program compiles fine with
the Borland compiler.

--
Thomas Matthews

C++ newsgroup welcome message:
http://www.slack.net/~shiva/welcome.txt
C++ Faq: http://www.parashift.com/c++-faq-lite
C Faq: http://www.eskimo.com/~scs/c-faq/top.html
alt.comp.lang.learn.c-c++ faq:
http://www.comeaucomputing.com/learn/faq/
Other sites:
http://www.josuttis.com -- C++ STL Library book

Jul 22 '05 #7

P: n/a
Thomas Matthews wrote:
Victor Bazarov wrote:
Thomas Matthews wrote:
Victor Bazarov wrote:

You can create a templated "table of widths" and only worry
about passing the name to your 'Name_Id' template and to the
'Name_Id_Table' template:
---------------------------------------------------------
template<const char* name> struct Name_Id_Aux_Info { enum { MaxWidth
}; };
template<typename T, const char* name> class Name_Id_Table;

template<const char* name> class Name_Id {
enum { MAX_STRING_WIDTH =
Name_Id_Aux_Info<name>::MaxWidth };

int foo() { return 42; } // private
friend class Name_Id_Table<Name_Id, name>;

At this point, my compiler, Borland Builder 6.0, gives me this
error:
E2299 Cannot generate template specialization from
'Name_Id_Table<T, name>'

Although Gnu G++ 3.3.1 compiles without any warnings or errors.

};

template<typename T, const char* name> class Name_Id_Table
{
public:
int bar(T& t) { return t.foo(); }
};

extern const char TITLE[] = "Title";
extern const char AUTHOR[] = "Author";
extern const char PUBLISHER[] = "Publisher";

template<> struct Name_Id_Aux_Info<TITLE> { enum { MaxWidth = 32 }; };
template<> struct Name_Id_Aux_Info<AUTHOR> { enum { MaxWidth = 64 }; };
template<> struct Name_Id_Aux_Info<PUBLISHER> { enum { MaxWidth = 96
}; };

typedef Name_Id<TITLE> id_Title;
typedef Name_Id_Table<id_Title, TITLE> id_Title_Table;

int main()
{
id_Title idt;
id_Title_Table idtt;

idtt.bar(idt);

return 0;
}


Is Gnu correct in this case or Borland?
{Generates same error with BCC32 version 5.6}
The code I posted compiles fine with Comeau online and with VC++ v7.1
which kind of suggests that the message Borland compiler emits is not
an indication of the error in the code but rather the admission of
BCBuilder's impotency.

Victor

I posted your program in a Borland newsgroup and they suggested
the following modification:

template<const char* name> class Name_Id {
enum { MAX_STRING_WIDTH =
Name_Id_Aux_Info<name>::MaxWidth };

int foo() { return 42; } // private
friend class Name_Id_Table<Name_Id<name>, name>;
/* ^^^^^^ */

This makes sense, but I don't know why the other compilers
don't require it.


You should ask Borland why they made it so that _their_ compiler _does_
require it.

The 'Name_Id' is the name of the template that is injected into the
class template scope. Every time you [try to] instantiate Name_Id,
it should become a concrete class, which then should specify which
specialisation of Name_Id_Table is used without the need to specify
the Name_Id's template argument explicitly, IMO.

[I am too lazy to look for a quote from the Standard.]

When I make the change, the program compiles fine with
the Borland compiler.


I'm glad you got it resolved.

Victor
Jul 22 '05 #8

This discussion thread is closed

Replies have been disabled for this discussion.