Oh, Interfaces?

For a long time I've needed to design certain classes without defining every local variable in the header, and just using functions for all manipulation. I think everyone else does it like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// Note: I haven't programmed for quite a while, so some of this might be invalid. Fortunately, my compiler will remind me.


// Definitions
class Apple
{
	virtual ~Apple () = 0;
	virtual unsigned int GetMass () = 0;
	virtual unsigned int Eat (unsigned int biteSize, unsigned int bites = 1) = 0;
};

Apple* NewApple (unsigned char scale = 127, unsigned char red = 230, unsigned char green = 10, unsigned char blue = 10);

// Implementation
class AppleStruct : public Apple
{
public:
	unsigned int mass;
	unsigned char red, green, blue;
};

unsigned int AppleStruct::GetMass ()
{
	return mass;
}

unsigned int AppleStruct::Eat (unsigned int biteSize, unsigned int bites)
{
	unsigned int ate = biteSize * bites;
	if (ate > mass) ate = mass;
	mass -= ate;

	return ate;
}

Apple *NewApple (unsigned char scale, unsigned char red, unsigned char green, unsigned char blue)
{
	Apple* apple = new AppleStruct;

	const unsigned int AVERAGE_MASS = 180; // Consider grams.
	apple->mass = (AVERAGE_MASS * (unsigned int)(scale + 1)) >> 7; // My first attempt at fixed-point math! Just for fun. A scale of 255 = double than average (127 = 1x).

	apple->red = red;
	apple->green = green;
	apple->blue = blue;

	return apple;
}

// Usage
Apple* greenApple = NewApple(255, 50, 240, 10);
unsigned int digest = greenApple->Eat(18, 3);

Is that how its done? For some reason I wanted to do this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Definitions
class Apple
{
	Apple (unsigned char scale = 127, unsigned char red = 230, unsigned char green = 10, unsigned char blue = 10);
	virtual ~Apple () = 0;
	unsigned int GetMass ();
	unsigned int Eat (unsigned int biteSize, unsigned int bites = 1);
};

// Implementation
// ... ?

// Usage
Apple greenApple(255, 50, 240, 10);
unsigned int digest = greenApple.Eat(18, 3);

Which I think is only possible if you use some kind of index (like a 'this' pointer?) that corresponds to a data structure stored in a map, but that's.. stupid.

So, I don't have much of a question. I'm just wondering how you implement 'interfaces.'
Last edited on
For the sake of actually trying this thing, I fixed up the code (some errors) and it works great. I created a full test:

Apple.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#ifndef _APPLE_H
#define _APPLE_H

class Apple
{
public:
	virtual ~Apple () {};
	virtual unsigned int GetMass () = 0;
	virtual unsigned int Eat (unsigned int biteSize, unsigned int bites = 1) = 0;
};

Apple* NewApple (unsigned char scale = 127, unsigned char red = 230, unsigned char green = 10, unsigned char blue = 10);

#endif 

Apple.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include "Apple.h"

class AppleStruct : public Apple
{
public:
	unsigned int GetMass ();
	unsigned int Eat (unsigned int biteSize, unsigned int bites);
	unsigned int mass;
	unsigned char red, green, blue;
};

unsigned int AppleStruct::GetMass ()
{
	return mass;
}

unsigned int AppleStruct::Eat (unsigned int biteSize, unsigned int bites)
{
	unsigned int ate = biteSize * bites;
	if (ate > mass) ate = mass;
	mass -= ate;

	return ate;
}

Apple* NewApple (unsigned char scale, unsigned char red, unsigned char green, unsigned char blue)
{
	AppleStruct* apple = new AppleStruct;

	const unsigned int AVERAGE_MASS = 180; // Consider grams.
	apple->mass = (AVERAGE_MASS * (unsigned int)(scale + 1)) >> 7; // My first attempt at fixed-point math! Just for fun. A scale of 255 = double than average (127 = 1x).

	apple->red = red;
	apple->green = green;
	apple->blue = blue;

	return apple;
}

Main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include "Apple.h"

extern "C"
{
	void printf (const char*, ...);
	void puts (const char*);
	int getchar ();
};

int main ()
{
	puts("Creating a green apple of 360 grams.");
	Apple* greenApple = NewApple(255, 50, 240, 10);

	printf("Ok. Current apple mass: %ig.\n\nNow taking three bites of the green apple. Each bite is 18g.\n", greenApple->GetMass());
	unsigned int digest = greenApple->Eat(18, 3);

	printf("Digesting %ig of apple.\nOk. Current apple mass: %ig.\n", digest, greenApple->GetMass());

	getchar(); // Termination signal (waits for enter key).

	return 0;
}


Execution results:
Creating a green apple of 360 grams.
Ok. Current apple mass: 360g.

Now taking three bites of the green apple. Each bite is 18g.
Digesting 54g of apple.
Ok. Current apple mass: 306g.
Last edited on
> For some reason I wanted to do this:
>
1
2
 // Usage
Apple greenApple(255, 50, 240, 10);



This is one way to support use of insulated Apple as a simple value type:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// header file: apple.h

class Apple
{
	explicit Apple( int mass = 127, int red = 230, int green = 10, int blue = 10 );
	Apple( const Apple& ) ;
	Apple( Apple&& ) noexcept ;
	Apple& operator= ( const Apple& ) ;
	Apple& operator= ( Apple&& ) noexcept;

	int GetMass() const noexcept ;
	int Eat( int biteSize, int bites = 1 ) ;

	private:
	   struct AppleStruct ; // opaque
	   std::unique_ptr<AppleStruct> implementation ;
};


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// Implementation file: apple.cc

struct Apple::AppleStruct
{
    AppleStruct( int m, int r, int g, int b ) : mass(m), red(r), green(g), blue(b) {}
    int mass ;
    int red, green, blue ;
    int GetMass() const noexcept { return mass ; }
    int Eat( int biteSize, int bites = 1 ) ;
    // ...
};

int Apple::AppleStruct::Eat( int biteSize, int bites )
{
    // TODO:
    return 0 ;
}

Apple::Apple( int mass, int red, int green, int blue )
    : implementation( new AppleStruct(mass,red,green,blue) ) {}

Apple::Apple( const Apple& that )
    : implementation( new AppleStruct( *that.implementation.get() ) ) {}

Apple::Apple( Apple&& that ) noexcept
    : implementation( std::move(that.implementation) ) {}

// Apple copy asssignment

// Apple move assignment

int Apple::GetMass() const noexcept { return implementation->GetMass() ; }

int Apple::Eat( int biteSize, int bites )
{ return implementation->Eat(biteSize,bites) ; }

// etc 

Last edited on
Oh, that's interesting! I was thinking about something like that. But by this way, you're making an association with the implementation directly from the class, rather than my idea of weaving them together invisibly with a map; also in the implementation (which is really stupid in several ways, your way is better). I wonder how similar the object code between these methods might look like, however I think I'll stay with my original method anyway. Thank you for your response, it was helpful.
Last edited on
Topic archived. No new replies allowed.