473,803 Members | 2,913 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

C++ Templates: Incomplete Type Problem

Greetings,

I hope everyone is enjoying the Holiday Season :) !

I'm attempting to implement a function template [called 'generate_while ']
modelled somewhat on the STL's, 'generate' and 'generate_n' algorithms. Now,
'generate_while ', requires the use of temporary storage [mind the
line-wrap]:

template <class OutputIter_, class Generator_, class Predicate_>
OutputIter_ generate_while( OutputIter_ first_,
Generator_& gen_, Predicate_ pred_)
{
typename std::iterator_t raits<OutputIte r_>::value_type value_(gen_());
for ( ; pred_(value_); ++first_)
{
*first_ = value_; value_ = gen_();
}
return first_;
}

and here is how I've instantiated it:

typedef ostreambuf_iter ator<char> StreamIterOut;
...
generate_while( StreamIterOut(c out),
mandel,
MandelbrotGener ator::Done(mand el));
...

As you can see I've attempted to use the iterator's type traits to obtain
the type on which the template function will operate. This appears to be a
legitimate approach to take [I used the example from section 7.5.1 of
Josuttis' Standard C++ Library]. The code [full code appears below at
message end] compiles using the Borland 5.51 compiler, and executes
correctly.

However, attempting to compile the same code under GCC 3.3.1 resulted in the
following errors:

mandelbrot.cpp: In function `OutputIter_ generate_while( OutputIter_,
Generator_&, Predicate_) [with OutputIter_ =
std::ostreambuf _iterator<char,
std::char_trait s<char> >, Generator_ = MandelbrotGener ator, Predicate_ =
MandelbrotGener ator::Done]':
mandelbrot.cpp: 93: instantiated from here
mandelbrot.cpp: 30: error: instantiation of `value_' as type `void'
mandelbrot.cpp: 30: error: `value_' has incomplete type
mandelbrot.cpp: 30: error: storage size of `value_' isn't known
mandelbrot.cpp: 32: error: no match for call to
`(MandelbrotGen erator::Done) (
<typeprefixerro r>&)'
mandelbrot.cpp: 64: error: candidates are: bool
MandelbrotGener ator::Done::ope rator()(char) const
mandelbrot.cpp: 34: confused by earlier errors, bailing out

A further check using the Comeau 'tryitout' compiler web-page emits the
following:

"ComeauTest .c", line 22: error: incomplete type is not allowed
typename std::iterator_t raits<OutputIte r_>::value_type value_(gen_());
^
detected during instantiation of "OutputIter _
generate_while( OutputIter_, Generator_ &, Predicate_)
[with OutputIter_=std ::ostreambuf_it erator<char,
std::char_trait s<char>>, Generator_=Mand elbrotGenerator ,
Predicate_=Mand elbrotGenerator ::Done]"

Quite obviously the code is doing something 'non-standard', not to mention
'unacceptable', to these later, more Standard-conforming compilers. What I
would greatly appreciate [aside from a solution / fix of course ;) !] is an
explanation of what appears to be going wrong as it's not at all clear to
me.

Cheers,

Anthony Borla

#include <cstdlib>
#include <complex>

#include <functional>
#include <algorithm>

#include <iterator>
#include <sstream>
#include <iostream>

using namespace std;

typedef complex<double> Complex;
typedef ostreambuf_iter ator<char> StreamIterOut;

/* ----------------------------- */

// STL 'lookalike' algorithm [based on STL's 'generate_n']
template <class OutputIter_, class Generator_, class Predicate_>
OutputIter_ generate_while( OutputIter_ first_, Generator_& gen_, Predicate_
pred_)
{
typename std::iterator_t raits<OutputIte r_>::value_type value_(gen_());

for ( ; pred_(value_); ++first_)
{
*first_ = value_; value_ = gen_();
}

return first_;
}

/* ----------------------------- */

class MandelbrotGener ator
{
public:
enum { EOS = -1 };

public:
MandelbrotGener ator(int height, int width);

bool done() const { return eos_; }

char next();
void reset();
void header(ostream& out) const;

operator bool() const { return !done(); }
char operator()() { return next(); }

public:
struct Done
{
public:
Done(Mandelbrot Generator& mref) : mref_(mref) { mref_.reset(); }
bool operator()(char ) const { return !mref_.done(); }

private:
MandelbrotGener ator& mref_;
};

private:
MandelbrotGener ator(const MandelbrotGener ator&);
MandelbrotGener ator& operator=(const MandelbrotGener ator&);

static int mandel(int n, const Complex& z, const Complex& c);

private:
int x_, y_, height_, width_;
bool eos_;
};

/* ----------------------------- */

int main(int argc, char* argv[])
{
ios_base::sync_ with_stdio(fals e);

if (argc != 2) { cerr << "Usage: " << argv[0] << " height"; return
EXIT_FAILURE; }
int n; if (!(istringstrea m(argv[1]) >> n) || n < 1) n = 100;

MandelbrotGener ator mandel(n, n);

mandel.header(c out);
generate_while( StreamIterOut(c out), mandel,
MandelbrotGener ator::Done(mand el));

return EXIT_SUCCESS;
}

/* ----------------------------- */

MandelbrotGener ator::Mandelbro tGenerator(int height, int width)
: x_(0), y_(0), height_(height) , width_(width), eos_(false)
{
}

/* ---------- */

char MandelbrotGener ator::next()
{
char byte = 0; int bitNumber = 0, limitMarker; bool output = false;

for ( ; y_ < height_; ++y_)
{
for ( ; x_ < width_; ++x_)
{
Complex z, c(2.0 * x_ / width_ - 1.5, 2.0 * y_ / height_ - 1.0);

limitMarker = mandel(50, z, c);

bitNumber += 1; if (bitNumber == 8) output = true;

byte = (byte << 1) | limitMarker;

if (x_ == width_ - 1 && bitNumber != 8)
{
byte = byte << (8 - width_ % 8); output = true;
}

if (output) { ++x_; return byte; }
}

x_ = 0;
}

eos_ = true ; return EOS;
}

/* ----------- */

void MandelbrotGener ator::reset()
{
x_ = 0; y_ = 0; eos_ = false;
}

/* ----------- */

void MandelbrotGener ator::header(os tream& out) const
{
out << "P4" << "\n" << width_ << " " << height_ << endl;
}

/* ----------- */

int MandelbrotGener ator::mandel(in t n, const Complex& z, const Complex& c)
{
if (real(z * conj(z)) > 4.0) return 0;
if (n == 0) return 1;
return MandelbrotGener ator::mandel(--n, z * z + c, c);
}
Dec 27 '05 #1
2 9921
In article <hh************ *******@news-server.bigpond. net.au>,
"Anthony Borla" <aj*****@bigpon d.com> wrote:
I hope everyone is enjoying the Holiday Season :) !
Thank you, likewise. :-)
I'm attempting to implement a function template [called 'generate_while ']
modelled somewhat on the STL's, 'generate' and 'generate_n' algorithms. Now,
'generate_while ', requires the use of temporary storage [mind the
line-wrap]:

template <class OutputIter_, class Generator_, class Predicate_>
OutputIter_ generate_while( OutputIter_ first_,
Generator_& gen_, Predicate_ pred_)
{
typename std::iterator_t raits<OutputIte r_>::value_type value_(gen_());
for ( ; pred_(value_); ++first_)
{
*first_ = value_; value_ = gen_();
}
return first_;
}


Section 24.3.1p2 of the standard says that the value_type of an output
iterator is void. Section 24.5.4 confirms this with the definition of
ostreambuf_iter ator (setting its value_type to void).

Imho, you're right and the standard is wrong. :-(

But it is what it is. There is a chance (that chance is growing smaller
every day) that C++0X will have improved iterator concepts, and this is
one detail I'd love to see fixed.

Here is one non-standard way that you can fix your code for gcc:

- typename std::iterator_t raits<OutputIte r_>::value_type value_(gen_());
+ __typeof__(gen_ ()) value_(gen_());

In C++0X we will likely have a standard keyword that looks a lot like
__typeof__ (not exactly) and can be used like:

decltype(gen_() ) value_(gen_());

That will do what you want as long as gen_() returns a non-reference
type. If it returns a reference type, then value_ will have reference
type (which may or may not be what you want).

Another (more standard) solution is to require of your predicate that it
follow the requirements laid down by std::unary_func tion: It has nested
types: argument_type and result_type. Then you could:

typename Predicate_::arg ument_type value_(gen_());

Personally I like the __typeof__ solution better, but it isn't as
portable.

For your amusement, here's an "almost solution" that we tried with TR1:

typename std::tr1::resul t_of<Generator_ ()>::type value_(gen_());

std::tr1::resul t_of might appear in a <functional> or <tr1/functional>
near you. It isn't standard. It is a standard's experiment.
Unfortunately in this particular case, result_of has a difficult time
deducing the correct return type. In gcc 4.x (if you change
<functional> to <tr1/functional>) AND if you add this line to
MandelbrotGener ator:

typedef char result_type;

then your code works. Without the result_type, gcc 4.x will again
default to void for this type. CodeWarrior 10 gets it right without the
result_type typedef.

There's no advice here, just entertainment. If you have to put
result_type into your generator anyway, you might as well just use it,
instead of taking the trip through result_of. And only some of the very
newest compilers will have this TR1 feature anyway.

Fwiw, you might consider rearranging your template arguments:

template <class Predicate_, class Generator_, class OutputIter_>
OutputIter_ generate_while( OutputIter_ first_, Generator_& gen_,
Predicate_
pred_)
{
....

I.e. consider which template argument you might ever want to let the
client code explicitly specify, and put that first. In the order I have
above, I've put the Predicate_ first, just to make it easy in case the
client would ever like to pass the predicate by reference instead of by
value:

MandelbrotGener ator::Done pred(mandel);
generate_while< MandelbrotGener ator::Done&>(St reamIterOut(cou t),
mandel, pred);
// inspect pred here ...

Just a thought.

Hope this helps.

-Howard
Dec 27 '05 #2
"Howard Hinnant" <ho************ @gmail.com> wrote in message
news:ho******** *************** ***********@syr cnyrdrs-03-ge0.nyroc.rr.co m...
In article <hh************ *******@news-server.bigpond. net.au>,
"Anthony Borla" <aj*****@bigpon d.com> wrote:

Howard,
I'm attempting to implement a function template [called
'generate_while '] modelled somewhat on the STL's,
'generate' and 'generate_n' algorithms. Now, 'generate_while ',
requires the use of temporary storage [mind the
line-wrap]:

template <class OutputIter_, class Generator_, class Predicate_>
OutputIter_ generate_while( OutputIter_ first_,
Generator_& gen_, Predicate_ pred_)
{
typename std::iterator_t raits<OutputIte r_>::value_type
value_(gen_());
for ( ; pred_(value_); ++first_)
{
*first_ = value_; value_ = gen_();
}
return first_;
}


Section 24.3.1p2 of the standard says that the value_type
of an output iterator is void. Section 24.5.4 confirms this
with the definition of ostreambuf_iter ator (setting its
value_type to void).

Imho, you're right and the standard is wrong. :-(

But it is what it is. There is a chance (that chance is
growing smaller every day) that C++0X will have improved
iterator concepts, and this is one detail I'd love to see fixed.


Well I certainly didn't *expect* an iterator type to be 'void'
[counter-intuitive to say the least] even though that's exactly what the
compiler diagnostics were indicating. Ah well !

<SNIP>

I opted for the '__typeof__' fix, thank you.

I realise now that I should have better investigated the matter [e.g. a
better reading of the GCC doco would have revealed the above fix], but I was
really thrown because I had a - surprisingly - trouble-free session with the
Borland compiler, and thought it a mere formality to recompile with GCC. I
really should have known better !

<SNIP>

Fwiw, you might consider rearranging your
template arguments:

template <class Predicate_, class Generator_, class OutputIter_>
OutputIter_ generate_while( OutputIter_ first_, Generator_& gen_,
Predicate_
pred_)
{
...

I.e. consider which template argument you might ever want to let the
client code explicitly specify, and put that first. In the order I have
above, I've put the Predicate_ first, just to make it easy in case the
client would ever like to pass the predicate by reference instead of by
value:

MandelbrotGener ator::Done pred(mandel);
generate_while< MandelbrotGener ator::Done&>(St reamIterOut(cou t),
mandel, pred);
// inspect pred here ...

Just a thought.


Yes, a far cleaner way of approaching it.

My thanks, Howard, for an informative, and very helpful, post. Enjoy the
rest of the Holiday Season :) !

Cheers,

Anthony Borla
Dec 28 '05 #3

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

7
4959
by: Andrew Ward | last post by:
Hi All, Considering the following code: struct A; struct B { std::list<A> l; };
1
3977
by: Bo Xu | last post by:
Object of Combination By Bo Xu Introduction A combination of n things, taken s at a time, often referred as an s-combination out of n, is a way to select a subset of size s from a given set of size n. There are n!/(s!(n-s)!) ways to do this. Donald E. Knuth gives several methods (algorithms) to generate all the s-combinations in . In such procedure-oriented way, each s-combination is processed while it's being generated. In some
4
4803
by: ro86 | last post by:
Hello everyone! I am a newbie to C++ (~1 Week experience) and I have a few months of experience with object-oriented languages (Objective-C). I am currently working just for fun on a particle system. All the particles are controlled by a "server". The server performs all kinds of operations on them (updating, drawing etc.). The particles (my "clients") on the other hand need to retrieve once a while some information from their server...
5
3738
by: Paul F. Dietz | last post by:
Is the following legal C? struct foo; struct foo (*p); /* Pointer to array of 10 foo structures */ struct foo { int bar; int baz; }; main() { printf("%d\n", sizeof(*p)); } Paul Dietz dietz@dls.net
6
7240
by: Pushkar Pradhan | last post by:
I tried to read the archives and solve this problem, but now I think I better post my problem: int main() { int blkSz = { {2,2}, {2,3}, ....., {6,6} }; write_bc_perf(mflops1, blkSz, NUMBASECASES);
2
2022
by: dave_dp | last post by:
Hi folks, I'm interested as to what extent using incomplete types doesn't result in a undefined behavior, more generally it touches the usage of incomplete types.. for example, it is stated that you can't use incomplete types to create objects of type T(that is incomplete), you can't use pointer arithmetic on T* types, you can't use new T..,..., you can't use sizeof... but you are allowed to have pointers and references to T... This is...
9
2664
by: Jerome Durand | last post by:
Hello, I'm trying to write something along the following lines but I cannot get this to compile. template <typename derivedstruct Base { typedef typename derived::valueType valueType; virtual valueType Value() = 0; };
5
5549
by: Belebele | last post by:
The code below does not compile under g++. There is an outer class template that contains and inner class template. The outer class template is fully specialized for int. Then a separate Foo class template inherits from the inner. The compiler indicates that the inner class is incomplete when used as the base class of the inner class template. Why? -------------------------------------------------------------------
50
4521
by: Juha Nieminen | last post by:
I asked a long time ago in this group how to make a smart pointer which works with incomplete types. I got this answer (only relevant parts included): //------------------------------------------------------------------ template<typename Data_t> class SmartPointer { Data_t* data; void(*deleterFunc)(Data_t*);
0
9703
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However, people are often confused as to whether an ONU can Work As a Router. In this blog post, we’ll explore What is ONU, What Is Router, ONU & Router’s main usage, and What is the difference between ONU and Router. Let’s take a closer look ! Part I. Meaning of...
0
9566
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it. First, let's disable language synchronization. With a Microsoft account, language settings sync across devices. To prevent any complications,...
0
10555
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers, it seems that the internal comparison operator "<=>" tries to promote arguments from unsigned to signed. This is as boiled down as I can make it. Here is my compilation command: g++-12 -std=c++20 -Wnarrowing bit_field.cpp Here is the code in...
0
10317
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven tapestry of website design and digital marketing. It's not merely about having a website; it's about crafting an immersive digital experience that captivates audiences and drives business growth. The Art of Business Website Design Your website is...
1
10300
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows Update option using the Control Panel or Settings app; it automatically checks for updates and installs any it finds, whether you like it or not. For most users, this new feature is actually very convenient. If you want to control the update process,...
1
7607
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new presenter, Adolph Dupré who will be discussing some powerful techniques for using class modules. He will explain when you may want to use classes instead of User Defined Types (UDT). For example, to manage the data in unbound forms. Adolph will...
0
5503
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The last exercise I practiced was to create a LAN-to-LAN VPN between two Pfsense firewalls, by using IPSEC protocols. I succeeded, with both firewalls in the same network. But I'm wondering if it's possible to do the same thing, with 2 Pfsense firewalls...
0
5636
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
1
4277
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated we have to send another system

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.