3
\$\begingroup\$

I've written this class that downloads an images, the URLs for which are read from a local .txt file. I'm keen to make sure my code is safe and efficient.

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include<cassert>
#include <algorithm>
#include <numeric>
#include <functional>
#include <curl/curl.h>
class DownloadTesting
{
 private:
 CURL *curl;
 FILE *file;
 CURLcode response;
 char outfilename[FILENAME_MAX];
 int count {0};
 std::vector<char*> URLS; //Stores the urls
 auto DownloadImage(const char *url) -> void;
 auto Convert(const std::string &s) -> char*;
 auto ReadDataStore() -> void;
 static auto WriteData(void *ptr, size_t size, size_t nmemb, FILE *stream) -> size_t;
 public:
 DownloadTesting();
 ~DownloadTesting();
 auto Interface() -> void;
};
DownloadTesting::DownloadTesting(){};
DownloadTesting::~DownloadTesting()
{
 if(!URLS.empty()) //Delete raw pointers
 {
 std::for_each(URLS.begin(),URLS.end(),[](char* url){delete url;});
 }
}
auto DownloadTesting::WriteData(void *ptr, size_t size, size_t nmemb, FILE *stream) -> size_t //Write data into file 
{
 size_t written {fwrite(ptr, size, nmemb, stream)};
 return written;
}
auto DownloadTesting::Convert(const std::string &s) -> char* //Convert url from std::string to raw char pointer
{
 char *pc {new char[s.size()+1]};
 std::strcpy(pc,s.c_str());
 return pc;
}
auto DownloadTesting::ReadDataStore() -> void
{
 std::vector<std::string> storage_vector;
 std::string storage_string;
 std::ifstream storage_file {"./imagestore/urlstore.txt"};
 if(storage_file.is_open()) //Open local file
 {
 while(getline (storage_file,storage_string)) //Read each line
 {
 storage_vector.push_back(storage_string); //Storage each line in vector
 }
 storage_file.close();
 }
 std::transform(storage_vector.begin(), storage_vector.end(), std::back_inserter(URLS), [this](const std::string url){return this->Convert(url);}); //Perform conversion function on each url
}
auto DownloadTesting::DownloadImage(const char *url) -> void
{
 count++; //Use a counter to name each downloaded file
 std::string name {std::to_string(count)}; //Convert int to string
 strcpy(outfilename, name.c_str()); //Convert std::string to [char]
 curl = curl_easy_init(); //Initialise curl
 if(curl)
 {
 file = fopen(outfilename,"wb");
 curl_easy_setopt(curl, CURLOPT_URL, url);
 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &DownloadTesting::WriteData);
 curl_easy_setopt(curl, CURLOPT_WRITEDATA, file);
 response = curl_easy_perform(curl);
 curl_easy_cleanup(curl); //Clean up curl
 fclose(file); //Close file
 }
}
auto DownloadTesting::Interface() -> void
{
 this -> ReadDataStore(); //Read in the data
 assert(!URLS.empty()); //Ensure data is not empty
 std::for_each(URLS.begin(),URLS.end(), [this](const char* url){return this -> DownloadImage(url);}); //Perform download for each url
}
int main()
{
 DownloadTesting DTO;
 DTO.Interface();
}
200_success
145k22 gold badges190 silver badges478 bronze badges
asked Dec 21, 2016 at 21:37
\$\endgroup\$
0

1 Answer 1

1
\$\begingroup\$

Language Features

You are trying to use new C++ language features, but you appear to be using them inefficiently.

  • This:

    std::for_each(URLS.begin(),URLS.end(), [this](const char* url){return this -> DownloadImage(url);});
    

    can be replaced by a simple:

    for (auto& url : URLS) DownloadImage(url)
    
  • Using auto for method declarations only to specify the return type at the end of the line is confusing.

Memory Management

Rule of 5: a class which needs a custom destructor for dealing with resource (de)allocations, most likely also needs a custom copy constructor, assignment operator and move constructor/assignment operator.

You are creating raw copies of strings only to store raw pointers to the characters. Simply use string.c_str() when you need to pass a raw pointer to the library and let the string class manage the memory.

CURL

Writing to the file via the callback and also asking curl to write the data to a file looks like duplicate work. Any reason for this?

OOP

The class isn't a good example of object-oriented design. The only available public method, Interface, doesn't indicate what it might do. The entire code might have been better off as a single function.

Jamal
35.2k13 gold badges134 silver badges238 bronze badges
answered Dec 21, 2016 at 22:09
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.