how to avoid temporaries when copying weakly typed object
- by Truncheon
Hi.
I'm writing a series classes that inherit from a base class using virtual. They are INT, FLOAT and STRING objects that I want to use in a scripting language. I'm trying to implement weak typing, but I don't want STRING objects to return copies of themselves when used in the following way (instead I would prefer to have a reference returned which can be used in copying):
a = "hello ";
b = "world";
c = a + b;
I have written the following code as a mock example:
#include <iostream>
#include <string>
#include <cstdio>
#include <cstdlib>
std::string dummy("<int object cannot return string reference>");
struct BaseImpl
{
    virtual bool is_string() = 0;
    virtual int get_int() = 0;
    virtual std::string get_string_copy() = 0;
    virtual std::string const& get_string_ref() = 0;
};
struct INT : BaseImpl
{
    int value;
    INT(int i = 0) : value(i)
    {
        std::cout << "constructor called\n";
    }
    INT(BaseImpl& that) : value(that.get_int())
    {
        std::cout << "copy constructor called\n";
    }
    bool is_string() { return false; }
    int get_int()
    {
        return value;
    }
    std::string get_string_copy()
    {
        char buf[33];
        sprintf(buf, "%i", value);
        return buf;
    }
    std::string const& get_string_ref()
    {
        return dummy;
    }
};
struct STRING : BaseImpl
{
    std::string value;
    STRING(std::string s = "") : value(s)
    {
        std::cout << "constructor called\n";
    }
    STRING(BaseImpl& that)
    {
        if (that.is_string())
            value = that.get_string_ref();
        else
            value = that.get_string_copy();
        std::cout << "copy constructor called\n";
    }
    bool is_string() { return true; }
    int get_int()
    {
        return atoi(value.c_str());
    }
    std::string get_string_copy()
    {
        return value;
    }
    std::string const& get_string_ref()
    {
        return value;
    }
};
struct Base
{
    BaseImpl* impl;
    Base(BaseImpl* p = 0) : impl(p) {}
    ~Base() { delete impl; }
};
int main()
{
    Base b1(new INT(1));
    Base b2(new STRING("Hello world"));
    Base b3(new INT(*b1.impl));
    Base b4(new STRING(*b2.impl));
    std::cout << "\n";
    std::cout << b1.impl->get_int() << "\n";
    std::cout << b2.impl->get_int() << "\n";
    std::cout << b3.impl->get_int() << "\n";
    std::cout << b4.impl->get_int() << "\n";
    std::cout << "\n";
    std::cout << b1.impl->get_string_ref() << "\n";
    std::cout << b2.impl->get_string_ref() << "\n";
    std::cout << b3.impl->get_string_ref() << "\n";
    std::cout << b4.impl->get_string_ref() << "\n";
    std::cout << "\n";
    std::cout << b1.impl->get_string_copy() << "\n";
    std::cout << b2.impl->get_string_copy() << "\n";
    std::cout << b3.impl->get_string_copy() << "\n";
    std::cout << b4.impl->get_string_copy() << "\n";
    return 0;
}
It was necessary to add an if check in the STRING class to determine whether its safe to request a reference instead of a copy:
Script code:
a = "test";
b = a;
c = 1;
d = "" + c; /* not safe to request reference by standard */
C++ code:
STRING(BaseImpl& that)
{
    if (that.is_string())
        value = that.get_string_ref();
    else
        value = that.get_string_copy();
    std::cout << "copy constructor called\n";
}
If was hoping there's a way of moving that if check into compile time, rather than run time.