News for hackers. Stuff that matters.

cmma

Re:Writing comments about pointer ownership on Sunday August 23, 2015 @06:02AM (#10105152)
The reason why that code fails is explained by gcc's unique_ptr implementation:

    /// Primary template of default_delete, used by unique_ptr
    template<typename _Tp>
      struct default_delete
      {
    ...
        void
        operator()(_Tp* __ptr) const
        {
          static_assert(!is_void<_Tp>::value,
                        "can't delete pointer to incomplete type");
          static_assert(sizeof(_Tp)>0,
                        "can't delete pointer to incomplete type");
          delete __ptr;
        }
      };
Basically, invoking "operator delete" on a pointer to an incomplete type (aka opaque pointer) is usually undefined. Calling "operator new" on an incomplete type is not possible (because its size is 0). Since unique_ptr deals with allocation/deallocation, and doesn't just copy pointer values around, it's not fully comparable to a plain pointer.

nice_byte proposed a good solution: if you can, abstract the new/delete logic to another file, in which the complete type of "Gadget" is known.

Re:Bash Pitfalls on Sunday August 16, 2015 @08:46PM (#10070881)
> You say you can do "some_command * .mp3"...true if either a) your command is safe to run with garbage input or b) you know that you have >= one file and no whitespace in filenames.

Globbing preserves whitespace. It's up to the target function to discard it, by forgetting to quote arguments.

    example1() {
        [ $# -eq 0 ] && return
        echo "$1"
        shift
        example1 "$@"
    }

    example2() {
        for arg in "$@"; do
            echo "$arg"
        done
    }

    touch 1 "2 3" 4
    example1 *
    example2 *

    # both examples print:
    #     1
    #     2 3
    #     4
Re:More Dirty Coding Tricks from Game Developers (2010) on Monday August 03, 2015 @07:52AM (#9995078)
In that case, the question should be, why am I attempting to convert one concrete type to another concrete type?

If you're writing a program, you should use the same type, std::vector<char* >, everywhere.

If you're writing a library, the API should avoid including container details in function parameters, and const and non-const T should be allowed.

    template<typename Container>
    void foo1(Container const& c);

    template<typename Iterator>
    void foo2(Iterator first);

    template<typename Iterator>
    void foo3(Iterator first, Iterator last);

    template<typename Iterator>
    void foo4(Iterator first, std::size_t num);

    template<typename T>
    void foo5(std::vector<T> const& vec);

    void foo6(char const* const* first, std::size_t num);
foo6 is a pretty good alternative here, imo:

    std::vector<char*> vec(...);
    foo6(&vec.front(), vec.size());
It would be cool if the compiler could figure templated type conversion out, but the crux of the issue is, as I understand it, that T<A> and T<A const> are not necessarily implemented the same way.

Also note that std::vector<std::string const> won't compile -- it wouldn't be copy-assignable -- so these issues mostly seem to arise specifically when dealing with C strings and/or C compatibility. And reinterpret_cast is amazingly helpful for porting from C.

Re:More Dirty Coding Tricks from Game Developers (2010) on Sunday August 02, 2015 @11:03PM (#9993503)
It sounds like you wanted to do something like this:

    std::vector<char*> vec1(...);
    std::vector<char const*> vec2(...);
    std::copy(vec1.begin(), vec1.end(), vec2.begin()); // works (1)
    std::copy(vec2.begin(), vec2.end(), vec1.begin()); // fails (2)
If this compiled, the second example would invoke undefined behavior and crash if you attempted to write to an element that pointed to read-only memory (e.g., a string literal).

Container element types are almost never declared const unless their values are truly immutable (in this case the pointer is mutable, but the underlying memory is not, which is kind of strange).

Re:More Dirty Coding Tricks from Game Developers (2010) on Sunday August 02, 2015 @10:19PM (#9993365)
Explain?

The most common usage of const in library code is for function parameters:

    Iterator find(T const& value) const;
The const actually _helps_ callers: find accepts both const and non-const references. A non-const argument will be implicitly cast to const by the compiler.

Compare that to:

    Iterator find(T& value) const;
This function signature accepts only non-const references (unless T is const). A caller must const_cast a const argument to use it (bad).

Return values are occasionally references:

    T& front();
    T const& front() const;
But "auto" makes the code shorter when the return type is const or unknown:

    auto& v = c.front(); // overload is deduced by the compiler based on the constness of c.