txesmi Ответов: 2

Пространство имен C++ не распознается компилятором в VS2017


Всем привет,
Я программист-любитель, привыкший писать программы для windows в скрипте стиля Си (liteC). Я перерабатываю себя, поэтому я учусь использовать C++. Я создаю небольшой проект в VS2017 для WIN32 со старым движком D3D9 и ImGui. У меня нет проблем понять, что такое классы, пространства имен и области видимости.

Я создаю файловый браузер с помощью std::experimental::filesystem, и все шло правильно, пока внезапно компилятор не перестал распознавать свое пространство имен. VS распознает его и не показывает никаких проблем с включением/пространством имен, но во время компиляции он отказывается распознавать пространство имен "std", которое было прекрасно распознано вчера вечером.

Я использую ярлык, подобный следующему, для модуля файловой системы, включенного в файл реализации класса:
namespace fs = std::experimental::filesystem;


Подсказка об ошибке:
Error	C2653	'fs': is not a class or namespace name	WMB7 Parser	g:\visual studio\wmb7 parser\wmb7 parser\filebrowser.h	22	


Сообщение об ошибке при замене ярлыка пространства имен полным путем к пространству имен:
Error	C3083	'experimental': the symbol to the left of a '::' must be a type	WMB7 Parser	g:\visual studio\wmb7 parser\wmb7 parser\filebrowser.h	22	
Error	C3083	'filesystem': the symbol to the left of a '::' must be a type	WMB7 Parser	g:\visual studio\wmb7 parser\wmb7 parser\filebrowser.h	22	


Что я мог сделать, чтобы он не узнал меня? Я заблудился.

Заранее спасибо.
ТКС.

---------------------------------------------------------------------
ОБНОВЛЕНИЕ

Просто заметил, что все работает так, как ожидалось при включении библиотеки файловой системы в основной заголовок вместо файла реализации класса. Ошеломляющий...

---------------------------------------------------------------------

Я почти уверен, что в источнике нет ошибок systax, но все равно вы идете:

Главная.ч
#pragma once

// Include the usual Windows headers
#define WIN32_LEAN_AND_MEAN		
#include <windows.h>

// Include D3D9
#include <d3d9.h>
#define DIRECTINPUT_VERSION 0x0800
#include <dinput.h>

// Include general standard library modules
#include <vector>
#include <string>

// Include ImGui
//#include "misc\freetype\imgui_freetype.h"
#include "imgui.h"
#include "imgui_impl_dx9.h"
#include "imgui_impl_win32.h"

// Include 3dgs data types, variables, and functions
#include "var.h"
#include "adll.h"

// 3dgs vars global pointer. Initialization on 'Core.cpp'
extern ENGINE_VARS *ev;

// Error message box
#define EBox(text) MessageBox(NULL, text, "CRITICAL_ERROR", 0);


Файловый браузер.ч
#pragma once

class FileBrowser {
public:
	FileBrowser();
	~FileBrowser();

	bool Init();
	void Close();

	bool GetFolderContent(const char *_chrRoot, const char *_chrFolder);
	bool Draw();

private:
	// Logical drives
	char *m_drives[33];
	char m_driveNames[99];
	long m_driveCount;
	long FindLogicalDrive(std::string _str);

	// Folder content
	fs::path m_pathRoot; // 22nd code line, the source of the unrecognized namespace
	fs::path m_pathFolder;
	std::vector<std::string> m_path;
	std::vector<std::string> m_content;

	// List box
	int m_current;
};


FileBrowser.cpp
#include "Main.h"

#include <experimental/filesystem>
//#include <filesystem>
//#include <Knownfolders.h>
//#include <comdef.h> 

#include "FileBrowser.h"

namespace fs = std::experimental::filesystem;

// ------------------------------------------------------------------------------------------------------------

FileBrowser::FileBrowser() {
	m_driveCount = 0;
	m_path.reserve(16);
	m_content.reserve(64);
	m_current = -1;
}

FileBrowser::~FileBrowser() {
}

// ------------------------------------------------------------------------------------------------------------

bool FileBrowser::Init() {
	// Clear string vectors
	m_path.clear();
	m_content.clear();

	// Logical drives
	memset(m_drives, 0, sizeof(char*) * 33);
	DWORD _logicalDrives = GetLogicalDrives();
	m_driveCount = 0;

	char *_chrT = m_driveNames;
	for (char _l = 0; _l < 32; _l += 1) {
		if (!(_logicalDrives & (1 << _l)))
			continue;
		m_drives[m_driveCount] = _chrT;
		*_chrT = 65 + _l; // drive letter
		*(_chrT + 1) = 58; // doble point
		*(_chrT + 2) = NULL;
		_chrT += 3;
		m_driveCount += 1;
	}

	// Init folder content
	GetFolderContent("", fs::current_path().u8string().c_str());

	return true;
}

void FileBrowser::Close() {
	m_path.clear();
	m_content.clear();
}

bool FileBrowser::GetFolderContent(const char *_chrRoot, const char *_chrFolder) {
	m_pathRoot = fs::u8path(_chrRoot);
	m_pathFolder = fs::u8path(_chrFolder);
	fs::path _pathFull;

	// Build the full path
	if (*_chrRoot == NULL) {
		_pathFull = m_pathFolder;
	} else {
		_pathFull = m_pathRoot;
		_pathFull += "\\";
		_pathFull += m_pathFolder;
	}
	if (!fs::exists(_pathFull))
		return false;
	if (!fs::is_directory(_pathFull))
		return false;

	// 
	size_t _folderCount = 0;
	for (const fs::directory_entry& _p : fs::directory_iterator(_pathFull)) {
		const fs::path& _path = _p.path();

		// avoid system and hidden files
		DWORD _attb = GetFileAttributesW(_path.wstring().c_str());
		if (_attb & FILE_ATTRIBUTE_HIDDEN)
			continue;
		if (_attb & FILE_ATTRIBUTE_SYSTEM)
			continue;

		// save the filename of the current path
		if (fs::is_directory(_path)) {
			m_content.insert(m_content.begin() + _folderCount, _path.filename().u8string().c_str());
			_folderCount += 1;
		} else {
			m_content.push_back(_path.filename().u8string().c_str());
		}
	}

	// decompose the folder path to its members
	fs::path _pathFolder = m_pathFolder;
	do {
		const std::string _str = _pathFolder.filename().u8string().c_str();
		if(_str.find("\\") == std::string::npos)
			m_path.insert(m_path.begin(), _str);
		_pathFolder = _pathFolder.parent_path();
	} while (_pathFolder.has_filename());

	return true;
}

long FileBrowser::FindLogicalDrive(std::string _str) {
	for (long _i = 0; _i < m_driveCount; _i += 1) {
		if (_str.find(m_drives[_i]) == std::string::npos)
			continue;
		return _i;
	}
	return -1;
}

bool PathContentGetter(void *_data, int _index, const char **_label) {
	std::vector<std::string> *_content = (std::vector<std::string>*)_data;
	*_label = (*_content)[_index].c_str();
	return true;
}

bool FileBrowser::Draw() {
	float _width = ImGui::GetContentRegionAvailWidth();

	// ----- CURRENT PATH BUTTONS -----
	fs::path _pathFull = "";
	for (unsigned long _i = 0; _i < m_path.size(); _i += 1) {
		fs::path _path = fs::u8path(m_path[_i]);
		// If the root of the current path is a logical drive
		if (_path.has_root_name()) {
			long _drive = FindLogicalDrive(_path.u8string().c_str());
			if (_drive < 0) {
				EBox("Unknown error on logical drives search");
				return false;
			} else {
				if (ImGui::BeginCombo("##DrivesCombo", m_drives[_drive], ImGuiComboFlags_NoArrowButton)) {
					// loop through the logical drives array
					for (long _ii = 0; _ii < m_driveCount; _ii += 1) {
						bool _isSelected = (_ii == _drive);
						if (ImGui::Selectable(m_drives[_ii], &_isSelected, NULL)) {
							//m_pathRoot = "";
							//m_pathFolder = m_drives[_ii];
							//m_pathFolder += "\\";
							//GetFolderContent(m_pathRoot.u8string().c_str(), m_pathFolder.u8string().c_str());
							//m_current = -1;
							EBox(m_drives[_ii]);
						}
					}
					ImGui::EndCombo();
				} else {
					_pathFull = m_drives[_drive];
				}
			}
		} else { // if it is not the root of the path
			if (_i != 0) {
				_pathFull += "\\";
			}
			_pathFull += _path;
			if (ImGui::Button(_path.u8string().c_str(), ImVec2(0, 0))) {
				//_pathFull = _path;
				//GetFolderContent(m_pathRoot.u8string().c_str(), _pathFull.u8string().c_str());
				EBox(_pathFull.u8string().c_str());
			}
		}
	}

	// ----- CURRENT PATH CONTENT LIST BOX -----
	ImGui::PushItemWidth(_width);

	if (ImGui::ListBox("##PathContentList", &m_current, PathContentGetter, &m_content, m_content.size(), 6)) {
		EBox(m_content[m_current].c_str());

	}

	return true;
}


Что я уже пробовал:

- Используйте полное пространство имен вместо ярлыка.
- Объявите пространство имен в заголовке класса. Что-то такое, что я читать не должен.
- Восстановите вчерашний код.
- Перезагрузите систему и IDE.
- Изменить порядок включения файлов.
- Не впадай в отчаяние хр

Richard MacCutchan

"он отказывается признавать пространство имен "std", которое было прекрасно распознано вчера вечером."
Так что же изменилось с тех пор и до сих пор? Я использовал это пространство имен на VS 2017 без проблем.

И было бы лучше, если бы вы просто разместили соответствующую часть кода и фактическое сообщение об ошибке, а не сбрасывали все и ожидали, что мы найдем, где могла произойти ошибка.

txesmi

Спасибо за ваш ответ. Приношу свои извинения за причиненные неудобства.
Код ошибки: Ошибка C2653 'ФС': не является именем класса или пространства имен WMB7 парсер g:\visual студия парсер\wmb7 парсер\wmb7\файловый браузер.ч. 22
Есть еще несколько строк ошибок, но все они связаны с ausence распознавания пространства имен.

Ничего не изменилось. Я восстановил код, который вчера просто отлично компилировался.

Приветствует,
txes

Richard MacCutchan

Только что проверил мой образец, и он использует именно эту конструкцию и строит нормально.

2 Ответов

Рейтинг:
5

Stefan_Lang

Всегда следите за тем, чтобы каждый заголовочный файл был самодостаточным. В данном случае это заголовок FileBrowser.h использует следующие символы, которые изначально не известны компилятору:

std::string
std::vector
fs
std::experimental::filesystem


Любой исходный файл, содержащий этот заголовок, должен будет убедиться, что эти символы предоставлены перед включением FileBrowser.h. Это очень утомительно и подвержено ошибкам, поэтому гораздо лучше включить все определения прямо в сам заголовочный файл:

#include <string>
#include <vector>
#include <experimental/filesystem>
namespace fs = std::experimental::filesystem;

class FileBrowser {
...

Иногда вы можете избежать фактического включения других заголовков, просто предоставив прямые объявления. Но это работает только до тех пор, пока у вас нет реальных членов данных, использующих эти символы: компилятор должен, по крайней мере, знать размер каждого члена данных, чтобы скомпилировать определение класса.


txesmi

Большое спасибо!
В конце концов, он компилируется.

Я читал, что не должен включать файлы в заголовки классов, но это противоречит решению. Мир самообучения очень узок.

Спасибо еще раз.
Здоровается!

Stefan_Lang

Ну да, вам следует избегать включения заголовков в заголовочный файл, если это возможно. Но здесь нет другого разумного пути. Вот почему я добавил этот последний абзац.

txesmi

Просто заметил, что все работает так, как ожидалось при включении библиотеки файловой системы в основной заголовок вместо файла реализации класса. Ошеломляющий...

Рейтинг:
13

Richard MacCutchan

У вас есть следующие утверждения в FileBrowser.cpp:

#include "Main.h"
#include "FileBrowser.h"

#include <experimental/filesystem>

Однако вы пытаетесь использовать fs пространство имен в файле FileBrowser.h, но оно не определено до тех пор, пока этот заголовочный файл не будет обработан. Измените порядок на
#include "Main.h"

#include <experimental/filesystem>
#include "FileBrowser.h"

и это должно сработать.


txesmi

Спасибо за ваш ответ. Я уже пробовал ваше решение, и возникла та же ошибка. Все равно попробовал еще раз, но то же самое.

Я подумываю о том, чтобы переустановить все это, хотя это заставляет меня хотеть взорвать все это...

Спасибо еще раз. Это действительно ценится.
Ваше здоровье!

Richard MacCutchan

Зачем ты это сделал? Тщательно исследуйте проблему и исправьте свои ошибки.

Вероятно, у вас есть та же проблема с Main.h, включенным перед файловой системой. Убедитесь, что вы поместили все системные включения перед любыми локальными включениями/определениями.

txesmi

I came here because I had already exhausted all my knowledge around and google findings. I followed your advise and checked everything and ensured that "Main.h" is included first and each class header last, been each dependencies in the middle. The project is in its very beginning and I just squeezed it down so it has only declarations and class objects creations. No more. The graphics engine and the UI engine is completelly out. It has only three nested classes: Core -> UserInterface -> FileBrowser, and they only create the next class object declarated on each. Been a 'std::experimental::filesystem::path' object as the last in the chain.
К сожалению, он не компилируется и выдает мне ту же ошибку в том же месте. Я ничего не изменил в свойствах проекта. Единственное, о чем я могу думать, это то, что что-то было испорчено в пути...

Stefan_Lang

Почти: в нем все еще отсутствует объявление символа fs.

txesmi

Привет, спасибо за ваш ответ.

Должен ли я объявить пространство имен в заголовке класса?

Stefan_Lang

Это не пространство имен, которое вам нужно объявить, а символ fs, который является псевдонимом пространства имен, объявленного в <experimental filesystem="">. Вот почему решение Ричардса изначально не работало: компилятор действительно распознал std::experimental::filesystem в тот момент, но он не знал, что означает символ fs.

Richard MacCutchan

Да, мне этого не хватало. Спасибо, что указал на это.