I see some things that may help you improve your code.
Fix the bug
The program loops infinitely (actually, as @CrisLuengo correctly points out in a comment, it's not actually infinite. However, it loops \$2^{54}\$ times for each 1024 bytes in the input file on a 64-bit machine, so it will certainly feel infinite for any human being waiting for the copy to complete!) if the output file already exists or can't be created. That's a bug that should be fixed. Generally, when a call can fail (as with open
) it is important to test the result for failure before acting on the result.
Use std::fstream
for portability
This could be a completely portable program using only standard C++ libraries. To do that, you'd omit <fcntl.h>
and <unistd.h>
and just use fstream
. I'm guessing you may have chosen to use the low-level functions for speed, but it's important to measure rather than assume. The results may surprise you!
Delete file before writing for speed
On several Linux platforms, overwriting an existing program is slower than creating one. For that reason, one way to make sure that the program is as fast as possible is to call std::remove
before doing the copy.
Return valid error codes
A value of 0
as an error indicates no error on Linux and many other platforms. If you execute cp
with no arguments, it returns with a value of 1
. If your program is intended to be an exact replacement, I'd recommend returning 1
instead of 0
if there are missing arguments.
Check for arguments using argc
We can determine if there are the correct number of arguments by examining argc
. The way you're doing it has a problem:
if ( argv[1] == nullptr || argv[2] == nullptr ){
The problem is that if there are zero arguments, argv[1]
and argv[2]
might not be pointing to anything, so dereferencing them is undefined behavior and therefore not good programming practice. In particular, you should be aware that argc
might be zero which would make testing argv[1]
undefined behavior.
Don't use std::string
if what you need is a char *
In this case, both of the command line arguments are turned into std::string
s but then used exclusively with c_str()
to turn them back int char *
. Better would be to simply use char *
without conversions to and from std::string
.
Prefer std::array
to std::vector
If the bounds of your std::vector
are known at compile-time (as in this case), then it's generally better to use std::array
rather than std::vector
. The std::vector
has provisions for resizing that this program doesn't use and doesn't need. In fact, for this program, there's actually not much difference between a plain C-style array and a std::array
.
Name your constant
The buffer size is 1024 bytes, which is probably a bit on the small size. I think it would be a slight improvement to increase the size and also to name that constant.
Think about non-file files
Under Linux, we have not just files but also devices and named pipes. Think about whether your program does what you intend if the input is a named pipe. What happens if we try to copy to /dev/null
?
An alternative version
Using most of those suggestions, here's an alternative:
#include <iostream>
#include <fstream>
#include <cstdio>
int main(int argc, char *argv[]) {
if (argc != 3) {
std::cout << "Usage: moderncp srcfile destfile\n";
return 1;
}
std::ifstream in{argv[1]};
std::remove(argv[2]);
std::ofstream out{argv[2]};
if (!out) {
std::cerr << "Could not create output file " << argv[2] << '\n';
return 1;
}
static constexpr std::size_t buffsize{1024};
char buffer[buffsize];
while (in.read(buffer, buffsize)) {
out.write(buffer, buffsize);
}
out.write(buffer, in.gcount());
}
Note that because we're using fstream
s for input and output, we don't have to explicitly close the files because the destructor will do so automatically as well as flushing the output buffer.
Results
On my 64-bit Linux machine, copying a 5.3GiB file took 1:29 with the original program, 1:25 with this rewritten version and 1:21 with cp
. Changing the rewritten version to use an 8K buffer allowed it to complete in 0:59, so that's clearly an improvement.
moderncp file1 file2
could becomemoderncp from to
\$\endgroup\$write()
that will turn into an infinite loop. You need to make sure you correctly check error codes on all C functions. \$\endgroup\$