1 Câu hỏi: `std :: filesystem :: path :: toán tử / (/ * args * /)` không hoạt động như mong đợi

câu hỏi được tạo ra tại Wed, May 8, 2019 12:00 AM

Tôi có một lớp với danh sách trình khởi tạo trong hàm tạo trong đó một trong các trường tôi đang khởi tạo là std::filesystem::path nhưng dường như nó không khởi tạo giá trị mong đợi.

MyClass::MyClass(
    unsigned int deviceSerial,
    const std::string& processName
) :
    deviceSerial(deviceSerial),
    processName(processName),
    configFilePath(GetBasePath() / std::to_string(deviceSerial) / ("#" + processName + ".json"))
{
    /* Parameter checks */
}

Sử dụng trình gỡ lỗi Tôi có thể thấy rằng GetBasePath() đang trả về chính xác những gì tôi mong đợi (trả về std::filesystem::path với đường dẫn chính xác) nhưng toán tử / dường như không có hiệu lực. Khi ở trong phần thân của hàm tạo, tôi có thể thấy rằng configFilePath được thiết lập theo kết quả của GetBasePath() mà không cần thêm thông tin bổ sung.

Tôi đang sử dụng MSVS-2019, tôi có tiêu chuẩn ngôn ngữ C ++ được đặt thành C ++ 17 và ở chế độ gỡ lỗi, tôi đã tắt tất cả các tối ưu hóa.

Tôi cũng đã thử nghiệm các phần sau trong phần thân của lớp và tôi vẫn thấy path đơn giản là kết quả của GetBasePath() và các mục bổ sung không được thêm vào.

{
    auto path = GetBasePath();             // path = "C:/Users/Me/Desktop/Devices"
    path /= std::to_string(deviceSerial);  // path = "C:/Users/Me/Desktop/Devices"
    path /= ("#" + processName + ".json"); // path = "C:/Users/Me/Desktop/Devices"
}

Một lưu ý nhỏ tôi cũng đã thử thử nghiệm ở trên với += thay vì /= và tôi vẫn thấy kết quả tương tự.

Chỉnh sửa

Theo yêu cầu, bên dưới là một ví dụ hoàn chỉnh và có thể kiểm chứng tối thiểu.

#include <Windows.h>
#include <cstdio>
#include <filesystem>
#include <memory>
#include <string>

std::string ExpandPath(const std::string &str) {
  auto reqBufferLen = ExpandEnvironmentStrings(str.c_str(), nullptr, 0);

  if (reqBufferLen == 0) {
    throw std::system_error((int)GetLastError(), std::system_category(),
                            "ExpandEnvironmentStrings() failed.");
  }

  auto buffer = std::make_unique<char[]>(reqBufferLen);
  auto setBufferLen =
      ExpandEnvironmentStrings(str.c_str(), buffer.get(), reqBufferLen);

  if (setBufferLen != reqBufferLen - 1) {
    throw std::system_error((int)GetLastError(), std::system_category(),
                            "ExpandEnvironmentStrings() failed.");
  }

  return std::string{buffer.get(), setBufferLen};
}

int main() {
  unsigned int serial = 12345;
  std::string procName = "Bake";

  std::filesystem::path p(ExpandPath("%USERPROFILE%\\Desktop\\Devices"));
  std::printf("Path = %s\n", p.string().c_str());
  // p = C:\Users\Me\Desktop\Devices

  p /= std::to_string(serial);
  std::printf("Path = %s\n", p.string().c_str());
  // p = C:\Users\Me\Desktop\Devices

  p /= "#" + procName + ".json";
  std::printf("Path = %s\n", p.string().c_str());
  // p = C:\Users\Me\Desktop\Devices

  std::getchar();
}

I've also used this example and tested with `p.append()` and got the same result.
    
2
  1. Bạn có thể vui lòng cung cấp một Ví dụ tối thiểu, đầy đủ và có thể xác minh .
    2019-05-08 16: 08: 40Z
  2. Đây là một dự đoán, nhưng hãy thử phương thức fs :: append ().
    2019-05-08 16: 21: 48Z
  3. "Sử dụng trình gỡ lỗi" Rất có thể đó là vấn đề của bạn. Làm thế nào dữ liệu đường dẫn được lưu trữ trong lớp là cụ thể triển khai và có thể cản trở mọi thứ từ bạn. Bạn nên đảm bảo rằng điều này có thể tái tạo thông qua giao diện của lớp (tức là in đường dẫn được nối tới bàn điều khiển)
    2019-05-08 20: 46: 41Z
  4. @ rustyx Vui lòng xem bản chỉnh sửa ở trên để biết ví dụ đầy đủ. @Mansoor Như bạn đề xuất, tôi cũng đã thử nghiệm path::append() và điều này cho kết quả tương tự @Frank Khi thực hiện ví dụ đã chỉnh sửa Tôi đã xóa tất cả các lớp trừu tượng và tôi cũng đã thử nghiệm bên ngoài trình gỡ lỗi khi được xây dựng ở chế độ phát hành. Vẫn không có gì khác biệt.
    2019-05-09 08: 00: 54Z
  5. Xin lỗi, đã bỏ lỡ #include trong MCVE, bây giờ sẽ biên dịch, mặc dù M.M có điểm tốt về lỗi đó :)
    2019-05-09 08: 19: 35Z
1 Câu trả lời                              1                         

Tôi muốn cảm ơn @rustyx và @Frank vì những gợi ý của họ, làm theo lời khuyên này đã khiến tôi phát hiện ra một lỗi trong cách tôi tạo chuỗi ban đầu được chuyển đến trình tạo đường dẫn (Cũng là @MM đã tìm thấy lỗi chính xác trong khi tôi đang gõ câu trả lời này)

Tôi đã tạo một hàm (được sử dụng trong lớp của tôi) std::string ExpandPath(const std::string& path) sử dụng API Windows để mở rộng bất kỳ biến môi trường nào trong một đường dẫn và trả về một chuỗi. Chuỗi này được tạo từ char* và số đếm, số đó bao gồm byte null nên khi tạo chuỗi bằng biến thể hàm tạo std::string(char* cstr, size_t len), điều này bao gồm byte null trong chính chuỗi.

Bởi vì tôi đang sử dụng trình gỡ lỗi để thẩm vấn các biến mà nó đọc các chuỗi kiểu C và dừng ở byte null. Trong ví dụ ban đầu của tôi, tôi cũng sử dụng printf() vì tôi chỉ thích chức năng này cho đầu ra, nhưng một lần nữa điều này dừng in ở byte null. Nếu tôi thay đổi đầu ra để sử dụng std::cout tôi có thể thấy rằng đầu ra có đường dẫn dự kiến ​​nhưng có thêm một khoảng trắng (byte rỗng được in dưới dạng khoảng trắng). Sử dụng std::cout Tôi thấy rằng các đường dẫn của tôi có kết quả như sau với mỗi phụ lục:

Path = C:\Users\Me\Desktop\Devices
Path = C:\Users\Me\Desktop\Devices \12345
Path = C:\Users\Me\Desktop\Devices \12345\#Bake.json
Tóm tắt

:

Lỗi trong của tôi ExpandPath() trong đó

  return std::string{buffer.get(), setBufferLen};

Nên

  return std::string{buffer.get(), setBufferLen - 1};
    
1
2019-05-09 08: 18: 02Z
nguồn đặt đây