views:

73

answers:

2

I've been playing around with type deduction/printing using templates with code of the form:

#include <iostream>
template <typename T>
class printType {};

template <typename T>
std::ostream& operator<<(std::ostream& os, const printType<T>&)
{
    os << "SomeType"; return os;
}  

template <typename T>
std::ostream& operator<<(std::ostream& os, const printType<T*>&)
{
    os << printType<T>() << "*"; return os;
}  

template <typename T>
std::ostream& operator<<(std::ostream& os, const printType<T&>&)
{
    os << printType<T>() << "&"; return os;
}  
// etc... and can call on a variable through

template <typename T>
printType<T> print(T) { return printType<T>(); }  

int main()
{
    int a = 7;
    int *p = &a;
    int &r = a;

    //OK: return SomeType*
    std::cout << "type of p: " << print(p) << std::endl;
    //Hmmmm: returns SomeType <- (no &: can I get around this?)
    std::cout << "type of r: " << print(r) << std::endl;
}

I am wondering whether or not I can get the last line to return int& , that is, either:
(i) have the function template print deduce the type of it's argument as int& or somehow work out it should return a printType<T&> when I pass it r; or
(ii)whether this is unavoidable because of the way the variable is passed to the function.

Are there any ways around this by changing the form of print or using some other template trickery? If solutions exists, I'd prefer non-C++0x, but is always good to see what short cuts will, if not already, be available in the future.

+3  A: 

There is no way to work this around. An expression p, where p names a reference, always has the type the reference refers to. No expression ever has type T&. So you cannot detect whether an expression originated from a reference or not.

This cannot be done with C++0x either. It's a deep principle of C++ that there are no expressions that have reference type. You can write decltype(r) to get the type of what r names instead of what type the expression r has. But you will not be able to write print(r), unless print is a macro of course, but I don't see why you would go that horrible road.

Johannes Schaub - litb
I suggest he cheats... see below.
Jason R. Mick
A: 

I take what I previously said back. I think I may have a way to make this work in pure c/c++, albeit in a very messy way. You would need to pass a pointer into your functions...

i.e. bool hello_world(std::string & my_string, const std::string * const my_string_ptr) {

bool hello_world(std::string my_string, const std::string * const my_string_ptr) {

if you now tested

if ( &my_string == my_string_ptr )

It would evaluate true if the var was passed by reference, and false if passed by value.

Of course doubling your variables in all your functions probably isn't worth it...


Johannes is right... not in pure c++. But you CAN do this. The trick is to cheat. Use an embedded scripting language like perl to search your source. Here's an embedded perl module:

http://perldoc.perl.org/perlembed.html

Pass it the function name, variable name, and source location and then use a regex to find the variable and check its type. Really this might be a better solution for your code in general, assuming you are always going to have a source handy.

I will post a function for this basic approach in a bit... gotta take care of some morning work! :)

Even if you don't want to distribute the source, you could create some sort of packed function/var data file that you could parse through @ runtime and get an equivalent result.


Edit 1

For example... using the # I32 match(SV *string, char *pattern) function in the Perl Embed tutorial, you could do something like:

bool is_reference(const char * source_loc, const char * function_name, 
                  const char * variable_name) {
   std::ifstream my_reader;
   char my_string[256];
   SV * perl_line_contents;
   bool ret_val = false;
   char my_pattern [400]=strcat("m/.*",function_name);
   my_pattern=strcat(my_pattern, ".*[,\s\t]*");
   my_pattern=strcat(my_pattern, variable_name);
   my_pattern=strcat(my_pattern, "[\s\t]*[\(,].*$");

   my_reader.open(source_loc.c_str());
   while (!my_reader.eof()) {
      my_reader.getline(my_string,256);
      sv_setpv(perl_line_contents,my_string);
      if(match(perl_line_contents,my_pattern)) {
          ret_val= true;
      }
   }

   return ret_val;
}

... there... two ways to do this (see above update).

Jason R. Mick