Function Problems

Pages: 1234
You cannot overload on the return type.
You can't override return types.

The main problem is that returning multiple types is not great design. Its okay for scripting languages that are just doing web-stuff. But for serious systems programming its way to dangerous.

The fact that it can't be done in C++ is a feature, not a limitation.

(don't mention the void*)
Effective limitations are NEVER features.

And indeed, it doesn't compile. I stand corrected (if C++ had an option for weak typing...).

-Albatross
You just love being contradictory, don't you?

My turn:
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
53
54
55
56
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
using namespace std;

struct return_t
  {
  string value;

  return_t( const string& value ): value( value ) { }

  operator string () const
    {
    return value;
    }

  operator double () const
    {
    double result = 0.0;
    istringstream iss( value );
    iss >> result;
    return result;
    }

  operator bool () const
    {
    return (value == "true");
    }
  };

enum type_t { T_BOOL, T_DOUBLE, T_STRING };

return_t f( type_t t )
  {
  switch (t)
    {
    case T_BOOL:   return return_t( "true" );
    case T_DOUBLE: return return_t( "3.141592" );
    case T_STRING: return return_t( "Hello world!" );
    }
  return return_t( "" );
  }

int main()
  {
  bool   b = f( T_BOOL );
  double d = f( T_DOUBLE );
  string s = f( T_STRING );

  cout << "boolean = " << boolalpha << b << "\n";
  cout << "double  = " << d << "\n";
  cout << "string  = " << s << "\n";

  return 0;
  }

Nyah.
@Duoas:

I need to get off my caffeine dependency.

However, I can't help but think that using void* would be a considerably shorter and better solution than this. You just need to ensure that you don't mess up on your explicit type casts.

Face it: those "evil" things are "evil" for a reason, and it's not because they're worse solutions to all given problems.

-Albatross
Thank you all, I went to bed shortly after my last post which is why it took me so long to reply and I also have somewhere to go later so I wouldn't recommended checking back between 08-18. Incedently I'm a guy so you don't need to refer to me as he/she. It's 5:40 my end so I wom't try any suggestions until later on in the day. If I do get it working then I'll be sure to post it for everyone to reference.
Effective limitations are NEVER features.


They are when they prevent you from making mistakes. Any programmer would rather have compiler errors than runtime errors.

The reason why void* is evil is because it sidesteps the compiler's [very involved] type systems and removes all the safeguarding. So if when you screw up, there's no warning or error from the compiler, and your program starts crashing, leaving you to debug thousands of lines code looking for a bad cast.
Last edited on
Disch wrote:
The reason why void* is evil is because it sidesteps the compiler's [very involved] type systems


They're easy to screw up, indeed, however that doesn't mean one should never use them.

EDIT: I think Duoas came closest to achieving what the OP wanted, though.

-Albatross
Last edited on
I still still think the original concept of a function taking fixed parameters but returning
a varying type is flawed.
Last edited on
You just need to ensure that you don't mess up on your explicit type casts.
I like to say that the best programmer is not the one who makes the least mistakes; it's the one who makes it the hardest for himself to make them, and minimizes the cost of making them.
Once you accept that you will make mistakes, you'll learn to work with that nature, not against it.

if C++ had an option for weak typing...
...it would be even harder to keep track of things.

They're easy to screw up, indeed, however that doesn't mean one should never use them.
There's very few cases where void * is an absolute necessity.
Right now I can only think of dynamic linking, "strongly" opaque pointers, and very specific cases where you're writing portable code and want to avoid conditional compilation (which would make it a special case of an opaque pointer).
I think that's about it.
Didn't notice page 2
As you might have noticed the function I'm creating is to use an array of Options with varying value types (reason for the type parameter), in the end I'll have 2 functions to handle the data: 1st will return the data, 2nd will change the data (this function I'll use when loading user options from a file). The 1st function's returned data will be handled by the main function by reading the type parameter before using it - this way I get the data I need and handle it in the approriate way.
Unions are another option. They're still very evil, but they're considerably less evil than void pointers.

Also note that unions don't play nice with complex types like string... so I intentionally put the string outside the union:

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
enum type_t { T_BOOL, T_DOUBLE, T_STRING };

struct return_t
{
  type_t type;
  union
  {
    bool vbool;
    double vdouble;
  };
  string vstring;   // don't put complex types in unions
};


return_t f(const string& data)
{
  return_t ret;
  if( /*data contains a bool*/ )
  {
    ret.type = T_BOOL;
    ret.vbool = /*...*/;
  }
  else if( /* data contains a double*/ )
  {
    ret.type = T_DOUBLE;
    ret.vdouble = /*...*/;
  }
  else
  {
    ret.type = T_STRING;
    ret.vstring = data;
  }

  return ret;
}
When I said it can't be done. I should have said that it can't be done UNLESS you are a dark magician like Duoas. ;-)
Last edited on
void pointers are just what they say on the tin. They point you into the void. The abyss. The dark depths of blackness...

...and nobody knows what's in there...
Last edited on
I'll go with Disch's approach but (1) I won't use unions and (2) to make up for the (possibly) big size of the ReturnValue type I'll pass it as a reference instead of directly returning it.

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
53
#include <iostream>
#include <string.h>
using namespace std;

struct ReturnValue
{
	static const int T_INT;
	static const int T_DOUBLE;
	static const int T_STRING;

	int ival;
	double dval;
	char sval[20];
};

const int ReturnValue::T_INT=0;
const int ReturnValue::T_DOUBLE=1;
const int ReturnValue::T_STRING=2;

void f(int type, ReturnValue & rval);

int main()
{
	ReturnValue ret_val;

	f(0,ret_val);
	cout << ret_val.ival << endl;

	f(1,ret_val);
	cout << ret_val.dval << endl;

	f(2,ret_val);
        cout << ret_val.sval << endl;

	cin.get();
	return 0;
}

void f(int type, ReturnValue & rval)
{
	switch (type)
	{
		case ReturnValue::T_INT:
			rval.ival=10; break;
		case ReturnValue::T_DOUBLE:
			rval.dval=5.5; break;
		case ReturnValue::T_STRING:
			strcpy(rval.sval,"asdf"); break;
		default:
          	        cout << "wrong type!" << endl;
     }

}
Last edited on
Duoas wrote:
You cannot overload on the return type.

Indeed, but instead of returning something you could pass it as a reference and then you can overload on it:

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
#include <iostream>
#include <string.h>
using namespace std;

void f(int & rval);
void f(double & rval);
void f(char * rval);

int main()
{
	int my_int;
	double my_double;
	char my_string[20];

	f(my_int);
	cout << my_int << endl;

	f(my_double);
	cout << my_double << endl;

	f(my_string);
	cout << my_string << endl;

	cin.get();
	return 0;
}

void f(int & rval)
{
	rval=10;
}

void f(double & rval)
{
	rval=5.5;
}

void f(char * rval)
{
	strcpy(rval,"asdf");
}
Last edited on
Okay, just to prove how evil spreads like a cancer, you've got me at it now.

Besides, we should have at least one template solution to this wicked design:

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
#include <iostream>
#include <sstream>

struct TYPE
{
	static const int INT;
	static const unsigned int UINT;
	static const float FLOAT;
	static const double DOUBLE;
	static const char CHAR;
	static const unsigned char UCHAR;
	static const char* CHAR_PTR;
	static const unsigned char* UCHAR_PTR;
};

const int TYPE::INT(0);
const unsigned int TYPE::UINT(0);
const float TYPE::FLOAT(0.0f);
const double TYPE::DOUBLE(0.0);
const char TYPE::CHAR('\0');
const unsigned char TYPE::UCHAR('\0');
const char* TYPE::CHAR_PTR(0);
const unsigned char* TYPE::UCHAR_PTR(0);

template<typename T>
T opt(const T&, const char* variable)
{
	T t;
	std::istringstream iss(variable);
	iss >> t;
	return t;
}

int main()
{
	int i = opt(TYPE::INT, "23");
	float f = opt(TYPE::FLOAT, "46.7");

	std::cout << "i = " << i << std::endl;
	std::cout << "f = " << f << std::endl;

	return 0;
}
Actually that's not completely working. function opt() may need to be overloaded to accommodate different conversiona types:

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
53
54
55
#include <iostream>
#include <sstream>
#include <cstring>

struct TYPE
{
	static const int INT;
	static const unsigned int UINT;
	static const float FLOAT;
	static const double DOUBLE;
	static const char CHAR;
	static const unsigned char UCHAR;
	static const char* CHAR_PTR;
	static const unsigned char* UCHAR_PTR;
};

const int TYPE::INT(0);
const unsigned int TYPE::UINT(0);
const float TYPE::FLOAT(0.0f);
const double TYPE::DOUBLE(0.0);
const char TYPE::CHAR('\0');
const unsigned char TYPE::UCHAR('\0');
const char* TYPE::CHAR_PTR(0);
const unsigned char* TYPE::UCHAR_PTR(0);


template<typename T>
T opt(const T&, const char* variable)
{
	T t;
	std::istringstream iss(variable);
	iss >> t;
	return t;
}

char* opt(const char*, const char* variable)
{
	return strcpy(new char[strlen(variable) + 1], variable);
}

int main()
{
	int i = opt(TYPE::INT, "23");
	float f = opt(TYPE::FLOAT, "46.7");
	char* c = opt(TYPE::CHAR_PTR, "hello");

	std::cout << "i = " << i << std::endl;
	std::cout << "f = " << f << std::endl;
	std::cout << "c = " << c << std::endl;

	delete c;

	return 0;
}
Last edited on
INT, UNIT, UCHAR_PTR, etc are bad choices for variable names since they're #defined as actual types by various libs (WinAPI)
Pages: 1234