1
template<typename T>
static T *anydup(const T *src, size_t len) {
    T *ptr = malloc(len * sizeof(T));
    memcpy(ptr, src, (len * sizeof(T)));
    return ptr;
}

Is this proper? Can I expect any errors from this when using an int, long, etc.? I'm very new to generic programming and am trying to learn more.

stenliis
  • 337
  • 8
  • 22
Phobos D'thorga
  • 439
  • 5
  • 17

1 Answers1

2

No this is not proper ! When you have a malloc() in C++ code, you should become very suspicious:

  • malloc() allocates memory, but doesn't properly create objects. The only way to work with such memory would be to use a placement new.
  • memcpy() doesn't respect the copy semantic of C++ objects. This could only work with trivially copiable classes. I would cause hard to find bugs elsewhere (shallow copies, and other awful things that lead to UB).

For basic types like char, int, double, it would work. But not for more complex types.


Alternative 1: adapt your code to properly create and copy objects

template<typename T>
T *anydup (const T *src, size_t len) {
    T *ptr = new T[len];                        // requires that T has a default constructor
    copy (src, src+len, ptr);                   // requires that T is copyiable
    return ptr;
}

Attention: risk of memory leakage if user forget to delete the array, or UB if user doesnet use delete[] ! To avoid this you could opt for returning unique_ptr<T[]>.


Alternative 2: Get rid of arrays and pointers and memory nightmares: use vectors !

template<typename T>
vector<T> anydup (const vector<T> src) {
    vector<T> v(len);                        // requires that T has a default constructor
    copy (src.cbegin(), src.cend(), v);      // requires that T is copyable
    return v;
}

You could consider creating the vector using a copy constructor as suggested by Remy Lebeau and FDinoff in the comments, either in the function or directly in the using code.

If you use copy() directly in the using code, you'll soon discover that there are also copy_if(), copy_backwards() and some other nice <algorithms> that could be used depending on circumstances.

Christophe
  • 68,716
  • 7
  • 72
  • 138
  • 1
    @PhobosD'thorga you're welcome ! please see my edit for alternative solutions. I didn't test it so there might be typos in it. – Christophe Sep 25 '16 at 18:35
  • Wow, thank you! And the examples are fantastic. I really have learned a heap with regard to merging the two worlds of C and C++ :) If I may ask one more thing, what does 'UB' stand for? – Phobos D'thorga Sep 25 '16 at 18:35
  • 5
    In Alternative 2. Please just use the copy constructor and don't create an anydup method. – FDinoff Sep 25 '16 at 18:36
  • 1
    @PhobosD'thorga UB means Undefined Behaviour (e.g. the kind of things that could happen if you dereference a NULL pointer -- sorry, a `nullptr`). - and yes C and C++ are close, but there are some key differences. In short, you need to understand the object model and the [rule of 3](https://en.wikipedia.org/wiki/Rule_of_three_(C%2B%2B_programming)). Many pointer solutions simply are no longer required if you replace `char*`with the more powerful `string`, and arrays with vectors. – Christophe Sep 25 '16 at 18:42
  • 1
    In alternative 2, you might consider using the vector constructor that accepts iterators as input: `template vector anydup (const vector &src) { return vector(src.cbegin(), src.cend()); }` – Remy Lebeau Sep 25 '16 at 18:47
  • 2
    Or even the copy constructor: `template vector anydup (const vector &src) { return vector(src); }` – Remy Lebeau Sep 25 '16 at 18:53
  • @RemyLebeau even better !! This shows that if we have a hammer in the hand, every problem look like a nail ;-) – Christophe Sep 25 '16 at 18:57
  • Your point about `malloc()` only being usable in combination with placement-new is not correct: `malloc()`-allocations can be used for objects that don't need construction. Like an array of `char`, for instance, or other plain-old-data objects. After all, all uses of `malloc()` are correct within a C program that is also valid C++, and none of those use placement-new by definition. – cmaster - reinstate monica Sep 25 '16 at 19:06
  • @cmaster thanks for giving me the opportunity to clarify: just look at my statement below the second bullet (*"For basic types like char, int, double, it would work"*). Nevertheless, the purpose of OP is to have a generic copy function, and malloc will not work for other types like for example `std::string`. Do you think there's any advantage in C++ to use `malloc()` for some types and `new` for other instead of using new in all the cases ? – Christophe Sep 25 '16 at 19:56
  • 1
    Well, my point was not about advantages or disadvantages, it was about what is possible. And, yes, a C++ program may need to use `malloc()` when it has to interact with pure C libraries. For instance, you cannot use `asprintf()` from C++ without also inserting at least one call to `free()` into your program. – cmaster - reinstate monica Sep 25 '16 at 20:33
  • I am interacting with pure C libraries, within a C++ program, actually. Good observation, @cmaster. – Phobos D'thorga Sep 25 '16 at 21:00
  • @cmaster ok ! C interoperability is a valid point. And that would imply the use of POD data which should not suffer from the lack of object model support. – Christophe Sep 25 '16 at 21:02