2009-09-03 43 views
111

Làm cách nào để ngăn không cho bao gồm tệp tiêu đề hai lần? Vấn đề là tôi đang bao gồm trong MyClass.h và sau đó tôi bao gồm MyClass.h trong nhiều tệp, vì vậy nó bao gồm nhiều lần và lỗi xác định lại xảy ra. Làm thế nào để ngăn chặn?C++ Các tệp tiêu đề định nghĩa lại (winsock2.h)

Tôi đang sử dụng #pragma một lần thay vì bao gồm cả bảo vệ và tôi đoán điều đó là tốt.

MyClass.h:

// MyClass.h 
#pragma once 

#include <winsock2.h> 

class MyClass 
{ 

// methods 
public: 
MyClass(unsigned short port); 
virtual ~MyClass(void); 
}; 

EDIT: Rất ít các lỗi tôi nhận được

c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(91) : warning C4005: 'AF_IPX' : macro redefinition 
     c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(460) : see previous definition of 'AF_IPX' 
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(124) : warning C4005: 'AF_MAX' : macro redefinition 
     c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(479) : see previous definition of 'AF_MAX' 
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(163) : warning C4005: 'SO_DONTLINGER' : macro redefinition 
     c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(402) : see previous definition of 'SO_DONTLINGER' 
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(206) : error C2011: 'sockaddr' : 'struct' type redefinition 
     c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(485) : see declaration of 'sockaddr' 
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(384) : error C2143: syntax error : missing '}' before 'constant' 
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(384) : error C2143: syntax error : missing ';' before 'constant' 
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(384) : error C2059: syntax error : 'constant' 
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(437) : error C2143: syntax error : missing ';' before '}' 
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(437) : error C4430: missing type specifier - int assumed. Note: C++ does not support default-int 
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(437) : error C4430: missing type specifier - int assumed. Note: C++ does not support default-int 
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(518) : warning C4005: 'IN_CLASSA' : macro redefinition 
     c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(287) : see previous definition of 'IN_CLASSA' 
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(524) : warning C4005: 'IN_CLASSB' : macro redefinition 
     c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(293) : see previous definition of 'IN_CLASSB' 
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(530) : warning C4005: 'IN_CLASSC' : macro redefinition 
     c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(299) : see previous definition of 'IN_CLASSC' 
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(541) : warning C4005: 'INADDR_ANY' : macro redefinition 
     c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(304) : see previous definition of 'INADDR_ANY' 
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(543) : warning C4005: 'INADDR_BROADCAST' : macro redefinition 
     c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(306) : see previous definition of 'INADDR_BROADCAST' 
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(577) : error C2011: 'sockaddr_in' : 'struct' type redefinition 
     c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(312) : see declaration of 'sockaddr_in' 
c:\program files\microsoft sdks\windows\v6.0a\include\winsock2.h(132) : error C2011: 'fd_set' : 'struct' type redefinition 
     c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(68) : see declaration of 'fd_set' 
c:\program files\microsoft sdks\windows\v6.0a\include\winsock2.h(167) : warning C4005: 'FD_SET' : macro redefinition 
     c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(102) : see previous definition of 'FD_SET' 
c:\program files\microsoft sdks\windows\v6.0a\include\winsock2.h(176) : error C2011: 'timeval' : 'struct' type redefinition 
     c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(111) : see declaration of 'timeval' 
c:\program files\microsoft sdks\windows\v6.0a\include\winsock2.h(232) : error C2011: 'hostent' : 'struct' type redefinition 
     c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(167) : see declaration of 'hostent' 
c:\program files\microsoft sdks\windows\v6.0a\include\winsock2.h(245) : error C2011: 'netent' : 'struct' type redefinition 
     c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(180) : see declaration of 'netent' 
c:\program files\microsoft sdks\windows\v6.0a\include\winsock2.h(252) : error C2011: 'servent' : 'struct' type redefinition 
     c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(187) : see declaration of 'servent' 
c:\program files\microsoft sdks\windows\v6.0a\include\winsock2.h(264) : error C2011: 'protoent' : 'struct' type redefinition 
     c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(199) : see declaration of 'protoent' 
+4

Bạn đã sử dụng #pragma một lần, vì vậy chỉ nên bao gồm một lần. – Naveen

+1

Trình biên dịch của bạn không hỗ trợ pragma một lần? –

+0

Tôi đang sử dụng Visual Studio 2008, tại sao sau đó được bao gồm hai lần? – akif

Trả lời

14

Bằng cách sử dụng "vệ sĩ tiêu đề":

#ifndef MYCLASS_H 
#define MYCLASS_H 

// This is unnecessary, see comments. 
//#pragma once 

// MyClass.h 

#include <winsock2.h> 

class MyClass 
{ 

// methods 
public: 
    MyClass(unsigned short port); 
    virtual ~MyClass(void); 
}; 

#endif 
+2

Tôi đoán là mình đã sai (4 upvotes), nhưng tôi nghĩ việc sử dụng bao gồm cả bảo vệ giống như pragma một lần, bạn đặt cả hai? –

+0

Vâng, tôi có #pragma một lần mà afaik là cùng một tiêu đề bảo vệ – akif

+1

@Angelov: Vâng, đó là những gì tôi đang nói rằng họ là những điều tương tự. Vấn đề không phải là với các tập tin tiêu đề của tôi, nhưng tôi nghĩ rằng chính nó không có bảo vệ tiêu đề hoặc có thể là cái gì khác. – akif

2

Bạn nên sử dụng bảo vệ tiêu đề.

đưa những dòng ở phía trên cùng của tập tin tiêu đề

#ifndef PATH_FILENAME_H 
#define PATH_FILENAME_H 

và ở dưới cùng

#endif 
+0

#pragma một lần và bao gồm các vệ sĩ là những thứ giống nhau phải không? – akif

+0

Chúng không hoàn toàn giống nhau - bộ phận bảo vệ tiêu đề sẽ ngăn chặn việc đưa lại tệp ở cấp tiền xử lý, cộng với chúng rõ ràng là dễ dàng hơn #pragma một lần. –

+0

Tôi có nghĩa là chúng được xây dựng cho cùng một mục đích :) – akif

1

#pragma once là bong, ngay cả trên MS trình biên dịch, và không được hỗ trợ bởi nhiều trình biên dịch khác. Như nhiều người khác đã đề cập, sử dụng bao gồm bảo vệ là con đường để đi. Không sử dụng #pragma once ở tất cả - nó sẽ làm cho cuộc sống của bạn dễ dàng hơn nhiều.

+2

Thật không may, tôi đã thấy nhiều hơn không có bảo vệ bao gồm, hoặc một lỗi chính tả có nghĩa là bảo vệ không thực sự làm việc, hoặc trong đó các tệp cùng tên trong các thư mục khác nhau sử dụng cùng một mã thông báo, hoặc nơi mã thông báo được sử dụng bắt đầu bằng dấu gạch dưới kép hoặc dấu gạch dưới thì chữ cái viết hoa (và do đó không di chuyển giống như #pragma một lần). Vì vậy, cho vốn không phải là mã di động, giống như bất cứ điều gì bằng cách sử dụng winsock.h, tôi đã vô cùng thất vọng bởi #pragma một lần phải lên đến điểm bạn nói nó là flakey. Khi nào nó thất bại, ngoài việc không được hỗ trợ chút nào? –

+3

Khi sử dụng '#pragma once', trình biên dịch sẽ lấy tên nút tệp tiêu đề là Id duy nhất. Điều này có thể thất bại nếu bạn có các liên kết tượng trưng hoặc các nút nối NTFS trong cây nguồn của bạn (phổ biến hơn bạn nghĩ), hoặc thậm chí nếu bạn có một tệp cùng tên trong một hệ thống khác bao gồm thư mục (điều này đã xảy ra với tôi trước khi tôi có phiên bản 1 và phiên bản 2 của cùng một thư viện được cài đặt cho hai hệ thống khác nhau bao gồm đường dẫn). Điểm mấu chốt: đối với tôi, tôi thích có nhiều quyền kiểm soát hơn và sống với lỗi thường xuyên khi sử dụng, thay vì tin tưởng một trình biên dịch để thực hiện điều đó cho tôi. – Thomi

2

#pragma once được dựa trên đường dẫn đầy đủ của tên tệp. Vì vậy, những gì bạn có thể có là có hai bản sao giống hệt nhau của MyClass.h hoặc Winsock2.h trong các thư mục khác nhau.

+0

một liên kết tượng trưng hoặc đường giao nhau NTFS cũng sẽ làm cho hệ thống bị hỏng. – Thomi

1

#include guards là cách tiêu chuẩn để thực hiện việc này. #pragma once không có nghĩa là không phải tất cả trình biên dịch đều hỗ trợ nó.

187

Sự cố này xảy ra khi bao gồm <windows.h> trước <winsock2.h>. Cố gắng sắp xếp gồm bạn danh sách đó <windows.h> được bao gồm sau <winsock2.h> hoặc xác định _WINSOCKAPI_ đầu tiên:

#define _WINSOCKAPI_ // stops windows.h including winsock.h 
#include <windows.h> 
// ... 
#include "MyClass.h" // Which includes <winsock2.h> 

Xem thêm this.

+0

Tôi không bao gồm ở tất cả, tôi biết thực hiện điều đó cho tôi. – akif

+2

Đối với tôi, mã của bạn biên dịch ok chỉ với '' trong MSVC2008. '' bao gồm làm cho nó tạo ra các lỗi biên dịch giống hệt như bạn đã cung cấp. – pingw33n

+0

Có phải là được bao gồm trong stdafx.h không? –

4

tôi sẽ không sử dụng chỉ FILENAME_H nhưng

#ifndef FILENAME_H_AF06570D_B36E_4B82_8F97_C456AF4A38FD 
#define FILENAME_H_AF06570D_B36E_4B82_8F97_C456AF4A38FD 

//code stuff 
#endif // FILENAME_H_AF06570D_B36E_4B82_8F97_C456AF4A38FD 

Tôi đã luôn luôn sử dụng một guid postfix. Tôi đã gặp một cơ sở mã rất kém một vài năm trước đây có các tệp tiêu đề khác nhau có cùng tên tệp và bao gồm cả bảo vệ. Các tệp được đề cập đã xác định một lớp có cùng tên. Nếu chỉ sử dụng không gian tên. Một số dự án đã biên soạn một số thì không. Sử dụng các nhân viên bảo vệ độc đáo là một phần của giải pháp trong việc phân biệt các tiêu đề và nội dung của chúng.

Trên Windows với Visual Studio sử dụng guidgen.exe, trên Linux uuidgen -t.

5

Tôi gặp sự cố này khi cố gắng kéo gói bên thứ ba dường như bao gồm cả cửa sổ.h một nơi nào đó trong đó lộn xộn của các tiêu đề. Xác định _WINSOCKAPI_ ở cấp độ dự án dễ dàng hơn nhiều (chưa kể đến việc duy trì được nhiều hơn) so với việc đi qua súp của họ và sửa chữa vấn đề bao gồm.

+1

Trên Qt, trong tệp .pro, có dạng như sau: 'DEFINES + = _WINSOCKAPI_' – phyatt

+0

@phyatt: bạn nên biến điều đó thành câu trả lời, nếu bạn không làm như vậy ! –

+0

@LeifGruenwoldt cho nó! Mừng vì tôi có thể giúp. – phyatt

11

Ồ - sự xấu xí của Windows ... Thứ tự bao gồm rất quan trọng ở đây. Bạn cần phải bao gồm winsock2.h trước windows.h. Kể từ windows.h có lẽ bao gồm từ tiêu đề biên dịch sẵn của bạn (stdafx.h), bạn sẽ cần phải bao gồm winsock2.h từ đó:

#include <winsock2.h> 
#include <windows.h> 
4

Tôi thấy liên kết này windows.h and winsock2.h trong đó có một sự thay thế mà làm việc tuyệt vời cho tôi:

#define _WINSOCKAPI_ // stops windows.h including winsock.h 
#include <windows.h> 
#include <winsock2.h> 

Tôi gặp khó khăn khi tìm sự cố xảy ra nhưng bằng cách thêm #define tôi có thể tạo mà không tìm ra.

62

Như những người khác đã đề xuất, vấn đề là khi windows.h được bao gồm trước WinSock2.h. Bởi vì windows.h bao gồm winsock.h. Bạn không thể sử dụng cả hai số WinSock2.hwinsock.h.

Giải pháp:

  • Bao gồm WinSock2.h trước windows.h. Trong trường hợp các tiêu đề được biên dịch sẵn, bạn nên giải quyết nó ở đó. Trong trường hợp của dự án đơn giản, nó rất dễ dàng. Tuy nhiên trong các dự án lớn (đặc biệt là khi viết mã di động, không có tiêu đề biên dịch trước), nó có thể rất khó, vì khi tiêu đề của bạn có WinSock2.h được bao gồm, windows.h có thể đã được đưa vào từ một số tệp tiêu đề/triển khai khác.

  • Xác định WIN32_LEAN_AND_MEAN trước windows.h hoặc dự án rộng. Nhưng nó sẽ loại trừ nhiều thứ khác bạn có thể cần và bạn nên bao gồm nó bằng của riêng bạn.

  • Xác định _WINSOCKAPI_ trước windows.h hoặc dự án rộng. Nhưng khi bạn bao gồm WinSock2.h bạn sẽ nhận được cảnh báo định nghĩa lại macro.

  • Sử dụng windows.h thay vì WinSock2.h là đủ cho dự án của bạn (trong hầu hết các trường hợp). Điều này có thể sẽ dẫn đến thời gian biên dịch lâu hơn nhưng giải quyết mọi lỗi/cảnh báo.

+12

'WIN32_LEAN_AND_MEAN' là giải pháp cho tôi xe tăng rất nhiều –

+0

Về giải pháp' _WINSOCK_': Bạn không nên grt cảnh báo redefinition macro nếu cả hai định nghĩa là giống nhau. Lỗi phổ biến là mọi người thêm định nghĩa vào dự án mà không đặt bất kỳ giá trị nào và mong đợi định nghĩa trống. Tuy nhiên, nếu bạn thêm '-D_WINSOCK_' vào dòng cmd, nó sẽ đặt' _WINSOCK_' thành 1. Để tạo định nghĩa rỗng, '-D_WINSOCK_ =' phải được chuyển. –

+0

Nếu bạn sử dụng '#define _WINSOCKAPI_', bạn cũng có thể cần' #define _WINSOCK_DEPRECATED_NO_WARNINGS', tùy thuộc vào hoàn cảnh của bạn. –

3

tôi đã chạy vào cùng một vấn đề và đây là những gì tôi đã phát hiện ra cho đến nay:

Từ đoạn đầu ra này -

 
c:\program files\microsoft sdks\windows\v6.0a\include\ws2def.h(91) : warning C4005: 'AF_IPX' : macro redefinition 
c:\program files\microsoft sdks\windows\v6.0a\include\winsock.h(460) : see previous definition of 'AF_IPX' 

-Nó vẻ như cả hai ws2def.h và winsock.h đã được đưa vào giải pháp của bạn.

Nếu bạn nhìn vào các tập tin ws2def.h nó bắt đầu với lời nhận xét sau -

/*++ 

Copyright (c) Microsoft Corporation. All rights reserved. 

Module Name: 

    ws2def.h 

Abstract: 

    This file contains the core definitions for the Winsock2 
    specification that can be used by both user-mode and 
    kernel mode modules. 

    This file is included in WINSOCK2.H. User mode applications 
    should include WINSOCK2.H rather than including this file 
    directly. This file can not be included by a module that also 
    includes WINSOCK.H. 

Environment: 

    user mode or kernel mode 

--*/ 

Chú ý đến dòng cuối cùng - "Tập tin này có thể không được bao gồm bởi một mô-đun mà cũng bao gồm WINSOCK.H "

Vẫn đang cố khắc phục sự cố mà không thực hiện thay đổi đối với mã.

Hãy cho tôi biết nếu điều này có ý nghĩa.

2

Tôi đã kiểm tra đệ quy bao gồm, tôi phát hiện các tập tin tiêu đề bao gồm (đệ quy) một số #include "windows.h" và "#include 'Winsock.h' and write a #include 'Winsock2.h' . in this files, i added #include 'Winsock2.h'` như đầu tiên bao gồm

Chỉ cần một vấn đề của sự kiên nhẫn, nhìn vào bao gồm từng người một và thiết lập trật tự này, trước hết #include "Winsock2.h" sau đó #include "windows.h"

1

trong dự án của tôi (tôi sử dụng VS 2008 SP1) hoạt động giải pháp tiếp theo:.

Tệp tiêu đề:

//myclass.h 
#pragma once 
#define _WINSOCKAPI_ 
#include <windows.h> 

Cpp lớp:

//myclass.cpp 
#include "Util.h" 
#include "winsock2class.h" 
#pragma comment(lib, "Ws2_32.lib") 

nơi #include "winsock2class.h" lớp mà thực hiện nghĩa winsock2.h:

//winsock2class.h 
#include <winsock2.h> 
#include <windows.h> 
#pragma comment(lib, "Ws2_32.lib") 
2

Trong VS 2015, sau đây sẽ làm việc:

#define _WINSOCKAPI_ 

Trong khi những điều sau đây sẽ không:

#define WIN32_LEAN_AND_MEAN 
Các vấn đề liên quan