Combined declarations and definitions in .h file

Member
Posts: 321
Joined: 2004.10
Post: #1
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.
Quote this message in a reply
Sage
Posts: 1,199
Joined: 2004.10
Post: #2
The file probably is missing something along the lines of:
Code:
#ifndef __MYFANCYCOLORSTRUCT_H__
#define __MYFANCYCOLORSTRUCT_H__

struct color {...};

#endif

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.
Quote this message in a reply
Member
Posts: 321
Joined: 2004.10
Post: #3
Nope. It has the following.

#ifndef COLOR_H
#define COLOR_H
Quote this message in a reply
Sage
Posts: 1,199
Joined: 2004.10
Post: #4
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.
Quote this message in a reply
Member
Posts: 321
Joined: 2004.10
Post: #5
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?

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
Quote this message in a reply
Moderator
Posts: 1,140
Joined: 2005.07
Post: #6
Which functions exactly does it say are redefined?
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #7
The code you have posted is fine, and won't generate multiply-defined errors [in and of itself].
Quote this message in a reply
Member
Posts: 321
Joined: 2004.10
Post: #8
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.

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?
Quote this message in a reply
Sage
Posts: 1,199
Joined: 2004.10
Post: #9
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:
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 Rasp
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #10
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]
Quote this message in a reply
Oldtimer
Posts: 834
Joined: 2002.09
Post: #11
Speaking of #pragma once - how portable is that? To me, it's sliced bread gone religious...
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #12
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.
Quote this message in a reply
Member
Posts: 321
Joined: 2004.10
Post: #13
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.
Quote this message in a reply
DoG
Moderator
Posts: 869
Joined: 2003.01
Post: #14
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.
Quote this message in a reply
Moderator
Posts: 1,560
Joined: 2003.10
Post: #15
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...
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  Dangling Pointers/ Memory Leak definitions... WhatMeWorry 1 4,079 Oct 3, 2005 10:43 AM
Last Post: OneSadCookie