Combined declarations and definitions in .h file
Code:
struct color {
Uint8 r, g, b, a;
color(Uint8 r_ = 0, Uint8 g_ = 0, Uint8 b_ = 0, Uint8 a_ = 255)
: r(r_), g(g_), b(b_), a(a_) {};
void set_gl_color() const { glColor4ub(r, g, b, a); }
void set_gl_color(Uint8 alpha) const { glColor4ub(r, g, b, alpha); }I've come across a very large C++/SDL/OpenGL project in sourceforge that was chock full of interesting code techniques etc that I want to study. To make it more managable, I wanted to create small "learning" projects with just a fraction of the code. So I started with a class particles.h and .cpp; but to get it to compile and link I need to put five other classes in my new project. All is well so far.
Now then, in my totally new main.cpp file I make instances of the stuff I'm playing around with and need to add new #includes. But I'm getting redefined function errors because the file above (which is a struct and not a class by the way).
I know I'm getting multiply defined function errors but don't know how to fix the problem. Could I separate the struct's declaration and put it in color.h and their definitions in a file called color.cpp?
Or is there an easier way? Also, is this one of the primary reasons that C++ code is split up into .h and .cpp files?
thanks.
The file probably is missing something along the lines of:
Normally, headers are wrapped in an include structure like above ( or a pragma ) to prevent being included multiple times via indirection. This might be what's going wrong.
Just guessing, really.
Code:
#ifndef __MYFANCYCOLORSTRUCT_H__
#define __MYFANCYCOLORSTRUCT_H__
struct color {...};
#endifNormally, headers are wrapped in an include structure like above ( or a pragma ) to prevent being included multiple times via indirection. This might be what's going wrong.
Just guessing, really.
Nope. It has the following.
#ifndef COLOR_H
#define COLOR_H
#ifndef COLOR_H
#define COLOR_H
WhatMeWorry Wrote:Nope. It has the following.
#ifndef COLOR_H
#define COLOR_H
Could you post the entire file?
Another possibility is that some function is being defined inline in the header, but not specified as inline. That doesn't affect class/struct member functions, but does affect functions defined outside.
Sure. Here it is. Actually, it has two structs but I just mentioned one in my first post.
Could I just change the structs into classes and then split up the declarations and definitions in .h and .cpp files?
Could I just change the structs into classes and then split up the declarations and definitions in .h and .cpp files?
Code:
#ifndef COLOR_H
#define COLOR_H
#ifdef WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
#ifdef USE_NATIVE_GL
#include <gl.h>
#else
#include "oglext/OglExt.h"
#endif
#include <SDL_types.h>
//#include <iostream>
///\brief Color representation with some basic transformations and OpenGL usage.
struct color {
Uint8 r, g, b, a;
color(Uint8 r_ = 0, Uint8 g_ = 0, Uint8 b_ = 0, Uint8 a_ = 255) : r(r_), g(g_), b(b_), a(a_) {};
void set_gl_color() const { glColor4ub(r, g, b, a); }
void set_gl_color(Uint8 alpha) const { glColor4ub(r, g, b, alpha); }
color(const color& c1, const color &c2, float scal) {
r = (Uint8)(c1.r*(1-scal) + c2.r*scal);
g = (Uint8)(c1.g*(1-scal) + c2.g*scal);
b = (Uint8)(c1.b*(1-scal) + c2.b*scal);
a = (Uint8)(c1.a*(1-scal) + c2.a*scal);
}
color operator* (const color& c) const {
return color(
(Uint8)(unsigned(r)*unsigned(c.r)/255),
(Uint8)(unsigned(g)*unsigned(c.g)/255),
(Uint8)(unsigned(b)*unsigned(c.b)/255),
(Uint8)(unsigned(a)*unsigned(c.a)/255)
);
}
void store_rgb(Uint8* ptr) const { ptr[0] = r; ptr[1] = g; ptr[2] = b; }
void store_rgba(Uint8* ptr) const { ptr[0] = r; ptr[1] = g; ptr[2] = b; ptr[3] = a; }
void store_rgb(float* ptr) const { ptr[0] = float(r)/255; ptr[1] = float(g)/255; ptr[2] = float(b)/255; }
void store_rgba(float* ptr) const { ptr[0] = float(r)/255; ptr[1] = float(g)/255; ptr[2] = float(b)/255; ptr[3] = float(a)/255; }
// transform color to grey value (model of human vision, 29.9% to 58.7% to 11.4% RGB)
float brightness() const { return (r*0.299+g*0.587+b*0.114)/255; }
color grey_value() const { Uint8 c = (Uint8)(r*0.299+g*0.587+b*0.114); return color(c, c, c, a); }
//color(istream& in) { r = read_u8(in); g = read_u8(in); b = read_u8(in); a = read_u8(in); }
//void save(ostream& out) const { write_u8(out, r); write_u8(out, g); write_u8(out, b); write_u8(out, a); }
// some useful standard colors
static color black() { return color(0,0,0); }
static color blue() { return color(0,0,255); }
static color green() { return color(0,255,0); }
static color red() { return color(255,0,0); }
static color magenta() { return color(255,0,255); }
static color cyan() { return color(0,255,255); }
static color yellow() { return color(255,255,0); }
static color orange() { return color(255,128,0); }
static color lightgrey() { return color(192,192,192); }
static color grey() { return color(128,128,128); }
static color darkgrey() { return color(64,64,64); }
static color white() { return color(255,255,255); }
};
///\brief Color representation with some basic transformations and OpenGL usage. Float values!
struct colorf {
float r, g, b, a;
colorf(float r_ = 0, float g_ = 0, float b_ = 0, float a_ = 1.0f) : r(r_), g(g_), b(b_), a(a_) {};
colorf(const color& c)
: r(c.r * 0.003921569f), g(c.g * 0.003921569f),
b(c.b * 0.003921569f), a(c.a * 0.003921569f) {} // 0.003921569 = 1/255
void set_gl_color() const { glColor4f(r, g, b, a); }
void set_gl_color(float alpha) const { glColor4f(r, g, b, alpha); }
colorf(const colorf& c1, const colorf& c2, float scal) {
r = (c1.r*(1-scal) + c2.r*scal);
g = (c1.g*(1-scal) + c2.g*scal);
b = (c1.b*(1-scal) + c2.b*scal);
a = (c1.a*(1-scal) + c2.a*scal);
}
colorf operator* (const colorf& c) const {
return colorf(r*c.r, g*c.g, b*c.b, a*c.a);
}
///> component wise linear interpolation
colorf lerp(const colorf& c1, const colorf& c2) const {
return colorf(c1.r*(1-r) + c2.r*r,
c1.g*(1-g) + c2.g*g,
c1.b*(1-b) + c2.b*b,
c1.a*(1-a) + c2.a*a);
}
void store_rgb(Uint8* ptr) const { ptr[0] = Uint8(r*255); ptr[1] = Uint8(g*255); ptr[2] = Uint8(b*255); }
void store_rgba(Uint8* ptr) const { ptr[0] = Uint8(r*255); ptr[1] = Uint8(g*255); ptr[2] = Uint8(b*255); ptr[3] = Uint8(a*255); }
void store_rgb(float* ptr) const { ptr[0] = r; ptr[1] = g; ptr[2] = b; }
void store_rgba(float* ptr) const { ptr[0] = r; ptr[1] = g; ptr[2] = b; ptr[3] = a; }
// transform color to grey value (model of human vision, 29.9% to 58.7% to 11.4% RGB)
float brightness() const { return (r*0.299+g*0.587+b*0.114); }
colorf grey_value() const { float c = (r*0.299+g*0.587+b*0.114); return colorf(c, c, c, a); }
color to_uint8() const { return color(Uint8(r*255), Uint8(g*255), Uint8(b*255), Uint8(a*255)); };
};
#endif
Which functions exactly does it say are redefined?
The code you have posted is fine, and won't generate multiply-defined errors [in and of itself].
I'm still at work, but I'll double check everything once I get home.
I did stumble across this rule on Rob Pike's (Unix/C/Bell Labs fame) web site.
What really got my attention was the phrase: "...what files they need to have included first..." Whoa, this implies that #include order is important? I thought that #ifdef's made everything foolproof?
For instance, is it illegal (uncompilable )to have A include B, B include C, and C include A?
And if so, would the use of #ifdefs solve the problem?
I did stumble across this rule on Rob Pike's (Unix/C/Bell Labs fame) web site.
Quote:Simple rule: include files should never include include files. If instead they state (in comments or implicitly) what files they need to have included first, the problem of deciding which files to include is pushed to the user (programmer) but in a way that's easy to handle and that, by construction, avoids multiple inclusions. Multiple inclusions are a bane of systems programming. It's not rare to have files included five or more times to compile a single C source file. The Unix /usr/include/sys stuff is terrible this way.
There's a little dance involving #ifdef's that can prevent a file being read twice, but it's usually done wrong in practice - the #ifdef's are in the file itself, not the file that includes it. The result is often thousands of needless lines of code passing through the lexical analyzer, which is (in good compilers) the most expensive phase
What really got my attention was the phrase: "...what files they need to have included first..." Whoa, this implies that #include order is important? I thought that #ifdef's made everything foolproof?
For instance, is it illegal (uncompilable )to have A include B, B include C, and C include A?
And if so, would the use of #ifdefs solve the problem?
It would be very hard to get away with having none of your h files include other h files. It's a noble goal, but would be a bit of a PITA. You can minimize it by prototyping classes or typedefs, etc. I wouldn't worry too much about it, unless you're writing a 1000 kloc app which takes a day to compile. Computers today are stupid fast, after all.
That being said, your last question:
It's illegal, and can really ruin an otherwise perfectly good day. I don't think ( in C/C++ at least ) that there's a way around circular include dependancies. ObjC's #import statement probably fixes all that in one fell swoop, but it's not portable to C/C++.
When you encounter this situation you'll basically have to carefully look at what's including what, and prototype classnames in one of the offending files. For example, in my situation, I've got ( for example ) a World class and an Entity class. Both need to deal with eachother -- e.g., World adds Entities, and Entities have pointers to the World they're in. But if World.h includes Entity.h, and Entity.h includes World.h I'd have a circular dependancy.
So, what I did was have World.h include Entity.h, but in Entity.h I just prototype the World class. In Entity.cpp I #include World.h.
It's all a PITA, but if you keep your mind on it while you structure your code, you can probably avoid it.
That being said, I have no idea why your code won't build
That being said, your last question:
Quote:For instance, is it illegal (uncompilable )to have A include B, B include C, and C include A?
It's illegal, and can really ruin an otherwise perfectly good day. I don't think ( in C/C++ at least ) that there's a way around circular include dependancies. ObjC's #import statement probably fixes all that in one fell swoop, but it's not portable to C/C++.
When you encounter this situation you'll basically have to carefully look at what's including what, and prototype classnames in one of the offending files. For example, in my situation, I've got ( for example ) a World class and an Entity class. Both need to deal with eachother -- e.g., World adds Entities, and Entities have pointers to the World they're in. But if World.h includes Entity.h, and Entity.h includes World.h I'd have a circular dependancy.
So, what I did was have World.h include Entity.h, but in Entity.h I just prototype the World class. In Entity.cpp I #include World.h.
It's all a PITA, but if you keep your mind on it while you structure your code, you can probably avoid it.
That being said, I have no idea why your code won't build
Personally, I *hate* stuff that's built with the "you figure out *all* the headers you need to include" philosophy -- it's just annoying to use, for a *very* minor compile-time performance improvement.
Use header guards, include headers within headers, and by and large you won't have any problems.
Circular includes won't cause problems, but they *indicate* problems -- there must be a single order the code can go in and compile. If you don't know that order, you need to work it out!
[edit]And #import works just fine in C/C++ (though it's not portable to other compilers) but it's not magic -- it's just the same as #include'ing a header with a unique header guard, or #including a header that has a #pragma once...[/edit]
Use header guards, include headers within headers, and by and large you won't have any problems.
Circular includes won't cause problems, but they *indicate* problems -- there must be a single order the code can go in and compile. If you don't know that order, you need to work it out!
[edit]And #import works just fine in C/C++ (though it's not portable to other compilers) but it's not magic -- it's just the same as #include'ing a header with a unique header guard, or #including a header that has a #pragma once...[/edit]
Speaking of #pragma once - how portable is that? To me, it's sliced bread gone religious...
Technically, not portable. In practice, GCC for Mac OS X, CodeWarrior, MSVC and ICC all seem to support it. I'm not sure about GCC for other platforms -- sure, it's supported, but it may produce a warning.
Problem solved! After 4 hours of simplifying. Turns out the problem was that when I created SDL OpenGL project in Xcode, the default is a main.c file. I then
dragged and dropped the various .h and .cpp files of classes. Exerything compiled and linked. However, when I tried to include any c++ headers in main.c I got lots of compiler errors which I assumed to be #include problems and thus my travails.
When I created a main.cpp, everthing worked like a charm.
Sorry to lead everybody down a wild goose chase. Thanks for all the
reminders of good programming practices.
I still don't know what Pike is talking about. I tried to move the includes out of includes and nothing would compile.
dragged and dropped the various .h and .cpp files of classes. Exerything compiled and linked. However, when I tried to include any c++ headers in main.c I got lots of compiler errors which I assumed to be #include problems and thus my travails.
When I created a main.cpp, everthing worked like a charm.
Sorry to lead everybody down a wild goose chase. Thanks for all the
reminders of good programming practices.
I still don't know what Pike is talking about. I tried to move the includes out of includes and nothing would compile.
Fenris Wrote:Speaking of #pragma once - how portable is that? To me, it's sliced bread gone religious...
GCC lists #pragma once as deprecated. Go figure. I've been using it instead of header guards across all kinds of platforms for years, and I will continue to do so until the day it breaks, as I do find the regular #ifdef headerguards lame.
This reminds me to get off my butt and write the tuturial on header management that I've been meaning to write for a while...
Possibly Related Threads...
| Thread: | Author | Replies: | Views: | Last Post | |
| Dangling Pointers/ Memory Leak definitions... | WhatMeWorry | 1 | 3,467 |
Oct 3, 2005 10:43 AM Last Post: OneSadCookie |
|

