পরিবর্তনশীল বার্তাগুলি সহ কীভাবে std :: ব্যতিক্রম করবেন?


121

আমি যখন ব্যতিক্রম কিছু তথ্য যুক্ত করতে চাই তখন আমি প্রায়শই এটির একটি উদাহরণ:

std::stringstream errMsg;
errMsg << "Could not load config file '" << configfile << "'";
throw std::exception(errMsg.str().c_str());

এটি করার কোন সুন্দর উপায় আছে?


10
আমি ভাবছি আপনি কীভাবে এইভাবে কাজ করতে সক্ষম হয়েছেন - আর্গ std∷exceptionদিয়ে কোনও কনস্ট্রাক্টর নেই char*
হাই-এঞ্জেল

2
আমি একই জিনিস অবাক করছি। হতে পারে এটি সি ++ এর কোনও মানহীন এমএস এক্সটেনশন? অথবা সি ++ 14 এ নতুন কিছু হতে পারে? বর্তমান ডকুমেন্টেশন std :: ব্যতিক্রম নির্মাণকারী কোন যুক্তি গ্রহণ করে না।
ক্রিস ওয়ার্থ

1
হ্যাঁ, তবে std::stringএতে একটি নিখুঁত নির্মাণকারী রয়েছে যা const char*...
ব্রাইস এম ডেম্পসে

6
@ ক্রিস ওয়ার্থ এটি std::exceptionশিশুদের ক্লাস প্রয়োগের নেপথ্যে 'এমএস'র অংশ বলে মনে হয় এবং তাদের সংস্করণ std::runtime_errorএবং এর সংস্করণ ব্যবহার করে std::logic_error। স্ট্যান্ডার্ড দ্বারা সংজ্ঞায়িত ছাড়াও, এমএসভিএস'র সংস্করণে <exception>আরও দু'জন কনস্ট্রাক্টর রয়েছে, একটি গ্রহণ (const char * const &)এবং অন্যজন গ্রহণ করে (const char * const &, int)। এগুলি একটি ব্যক্তিগত ভেরিয়েবল সেট করতে ব্যবহৃত হয় const char * _Mywhat; যদি তা ফেরত দেয় _Mywhat != nullptrতবে what()ডিফল্ট হয়। কোড যা এটির উপর নির্ভর করে সম্ভবত পোর্টেবল নয়।
জাস্টিন সময় - মনিকা

উত্তর:


49

এখানে আমার সমাধান:

#include <stdexcept>
#include <sstream>

class Formatter
{
public:
    Formatter() {}
    ~Formatter() {}

    template <typename Type>
    Formatter & operator << (const Type & value)
    {
        stream_ << value;
        return *this;
    }

    std::string str() const         { return stream_.str(); }
    operator std::string () const   { return stream_.str(); }

    enum ConvertToString 
    {
        to_str
    };
    std::string operator >> (ConvertToString) { return stream_.str(); }

private:
    std::stringstream stream_;

    Formatter(const Formatter &);
    Formatter & operator = (Formatter &);
};

উদাহরণ:

throw std::runtime_error(Formatter() << foo << 13 << ", bar" << myData);   // implicitly cast to std::string
throw std::runtime_error(Formatter() << foo << 13 << ", bar" << myData >> Formatter::to_str);    // explicitly cast to std::string

1
ওমগ আমি এই জাতীয় কিছু করতে খুঁজছি। তবে সম্ভবত ওভার- (অপারেটর ওভারলোডিং) রোধ করতে স্পষ্ট ফাংশনে অপারেটরকে পরিবর্তন করবে
রোমান প্লিল

3
এটি এবং একটি স্ট্যান্ড :: স্ট্রিমস্ট্রিমের মধ্যে পার্থক্য কী? এটিতে একটি স্ট্রিংস্ট্রিম রয়েছে বলে মনে হয় তবে এতে কোনও অতিরিক্ত কার্যকারিতা নেই (যতদূর আমি বলতে পারি)।
matts1

2
সাধারণত, এটি 100% নিরাপদ উপায় নয়। স্ট্যান্ড :: স্ট্রিংস্ট্রিম পদ্ধতিগুলি একটি ব্যতিক্রম ছুঁড়ে ফেলতে পারে problem সমস্যাটি এখানে বর্ণিত বেশ ভাল: বুস্ট.আর.কম
আর্থার পি।

1
@ আর্থারপি.গলুভ তবে এই ক্ষেত্রে, একটি ফর্ম্যাটর () উদাহরণও পর্দার আড়ালে একটি স্ট্রিংস্ট্রিম ইনস্ট্যান্ট করে, যা আবারও একটি ব্যতিক্রম ছুঁড়ে ফেলতে পারে। তাহলে পার্থক্য কী?
জুজু কর্নেলিউ

একমাত্র যুক্ত কার্যকারিতা হ'ল কনভার্টটোস্ট্রিং কৌশল এবং স্প্রিং কাস্টিং স্ট্রিংয়ে, যাইহোক দুর্দান্ত। ;)
জুলু কর্নেলিউ

178

মান ব্যতিক্রমগুলি একটি থেকে তৈরি করা যেতে পারে std::string:

#include <stdexcept>

char const * configfile = "hardcode.cfg";
std::string const anotherfile = get_file();

throw std::runtime_error(std::string("Failed: ") + configfile);
throw std::runtime_error("Error: " + anotherfile);

মনে রাখবেন যে, বেস বর্গ std::exceptionকরতে না এইভাবে নির্মাণ করা; আপনাকে একটি কংক্রিট, উত্পন্ন ক্লাস ব্যবহার করতে হবে।


27

সেখানে যেমন বিভিন্ন ব্যতিক্রম আছে runtime_error, range_error, overflow_error, logic_error, ইত্যাদি .. আপনি তার কন্সট্রাকটর মধ্যে স্ট্রিং পাস করতে হবে, এবং আপনি কনক্যাটেনেট করতে যাই হোক না কেন আপনার বার্তাটি করতে চাই। এটি কেবল একটি স্ট্রিং অপারেশন।

std::string errorMessage = std::string("Error: on file ")+fileName;
throw std::runtime_error(errorMessage);

আপনি এটির boost::formatমতো ব্যবহার করতে পারেন :

throw std::runtime_error(boost::format("Error processing file %1") % fileName);

উপরোক্ত বুস্ট :: ফর্ম্যাট সংস্করণটি সুস্পষ্ট রূপান্তর ছাড়াই সংকলন করবে না, যেমন: রানটাইম_অরর ((বুস্ট :: ফর্ম্যাট ("পাঠ্য% 1"% 2) .str ()))। সি ++ ২০ একটি স্ট্যান্ড :: ফর্ম্যাট প্রবর্তন করে যা অনুরূপ কার্যকারিতা সরবরাহ করবে।
ডিজিগ্র্যাট

17

নিম্নলিখিত শ্রেণিটি বেশ কার্যকর হতে পারে:

struct Error : std::exception
{
    char text[1000];

    Error(char const* fmt, ...) __attribute__((format(printf,2,3))) {
        va_list ap;
        va_start(ap, fmt);
        vsnprintf(text, sizeof text, fmt, ap);
        va_end(ap);
    }

    char const* what() const throw() { return text; }
};

ব্যবহারের উদাহরণ:

throw Error("Could not load config file '%s'", configfile.c_str());

4
খারাপ অনুশীলন আইএমও, ইতিমধ্যে যখন একটি আদর্শ লাইব্রেরি রয়েছে যা অপ্টিমাইজেশনের জন্য নির্মিত হয় তখন কেন এমন কিছু ব্যবহার করবেন?
জিন-মেরি ধূমকেতু

3
throw std::runtime_error(sprintf("Could not load config file '%s'", configfile.c_str()))
জিন-মেরি ধূমকেতু

4
throw std::runtime_error("Could not load config file " + configfile);(প্রয়োজনে এক বা অন্য যুক্তিকে রূপান্তর করা std::string)।
মাইক সিমুর

9
@ মাইকসিমুর হ্যাঁ, তবে যদি আপনাকে মাঝখানে কিছু নির্দিষ্ট নির্ভুলতা সহ ফর্ম্যাট সংখ্যার মধ্যে স্ট্রিংগুলি লাগাতে হয় তবে এটি সুদৃ ..় হয় cla
ম্যাক্সিম এগারুশকিন

2
@ মাইকসিমুর আমি সম্মত হতে পারি যে আমি পোস্ট করা কোডটি তার সময়ের আগে হতে পারে। বহনযোগ্য টাইপসএফ printfএবং বন্ধুরা সি ++ 11 এ আসন্ন। স্থির আকারের বাফার একটি আশীর্বাদ এবং একটি অভিশাপ উভয়ই: এটি কম সংস্থার পরিস্থিতিতে ব্যর্থ হয় না তবে বার্তাটি কেটে যেতে পারে। আমি একটি ত্রুটি বার্তা ছাঁটাই একটি ভাল বিকল্প তারপর ব্যর্থ বিবেচনা। এছাড়াও, ফর্ম্যাট স্ট্রিংগুলির সুবিধাদি বিভিন্ন বিভিন্ন ভাষা দ্বারা প্রমাণিত হয়েছে। তবে আপনি ঠিক বলেছেন, এটি মূলত স্বাদের বিষয়।
ম্যাক্সিম এগুরুশকিন

11

C ++ 14 ( operator ""s) স্ট্রিং আক্ষরিক অপারেটর ব্যবহার করুন

using namespace std::string_literals;

throw std::exception("Could not load config file '"s + configfile + "'"s);

অথবা সি ++ 11 এ থাকলে নিজের নিজস্ব সংজ্ঞা দিন। এই ক্ষেত্রে

std::string operator ""_s(const char * str, std::size_t len) {
    return std::string(str, str + len);
}

আপনার থ্রো বিবৃতিটি এর পরে দেখতে পাবেন

throw std::exception("Could not load config file '"_s + configfile + "'"_s);

যা দেখতে সুন্দর এবং পরিষ্কার দেখাচ্ছে।


2
আমি এই ত্রুটি পেয়েছিলাম C ++ \ 7.3.0 \ বিট \ exception.h | 63 | নোট: 'থেকে এসটিডি :: ব্যতিক্রম কল :: ব্যতিক্রম (এসটিডি :: __ cxx11 :: basic_string <গৃহস্থালির কাজ>) জন্য আপনার সাথে মেলে এমন ফাংশন
হাসিব মীর

@ শ্রীভর্ধনের বর্ণিত আচরণটি স্ট্যান্ড লাইব্রেরিতে সংজ্ঞায়িত করা হয়নি, যদিও এমএসভিসি ++ এটি সংকলন করবে।
জোচেন

0

সত্যিই সুন্দর উপায়টি ব্যতিক্রমগুলির জন্য একটি শ্রেণি (বা ক্লাস) তৈরি করা হবে।

কিছুটা এইরকম:

class ConfigurationError : public std::exception {
public:
    ConfigurationError();
};

class ConfigurationLoadError : public ConfigurationError {
public:
    ConfigurationLoadError(std::string & filename);
};

কারণটি হ'ল ব্যতিক্রমগুলি কেবল একটি স্ট্রিং স্থানান্তর করার চেয়ে অনেক বেশি পছন্দনীয়। ত্রুটির জন্য বিভিন্ন শ্রেণি সরবরাহ করে, আপনি বিকাশকারীদের একটি নির্দিষ্ট ত্রুটিটিকে অনুরূপভাবে পরিচালনা করার সুযোগ দেন (কেবল একটি ত্রুটি বার্তা প্রদর্শন করবেন না)। লোকেরা যদি আপনার শ্রেণিবিন্যাস ব্যবহার করে তবে আপনার ব্যতিক্রমটিকে ধরার জন্য তারা যেমন প্রয়োজন তেমন সুনির্দিষ্ট হতে পারে।

ক) নির্দিষ্ট কারণ জানা প্রয়োজন হতে পারে

} catch (const ConfigurationLoadError & ex) {
// ...
} catch (const ConfigurationError & ex) {

ক) অন্য কোনও বিবরণ জানতে চায় না

} catch (const std::exception & ex) {

আপনি https://books.google.ru/books?id=6tjfmnKhT24C অধ্যায় 9 এ এই বিষয়ে কিছু অনুপ্রেরণা খুঁজে পেতে পারেন

এছাড়াও, আপনি একটি কাস্টম বার্তাও সরবরাহ করতে পারেন তবে সাবধান হন - কোনও std::stringবা std::stringstreamঅন্য কোনও উপায় যা কোনও ব্যতিক্রম ঘটতে পারে তার সাথে কোনও বার্তা রচনা করা নিরাপদ নয়

সাধারণত, আপনি ব্যতিক্রমের নির্মাণকারীর মধ্যে মেমরি বরাদ্দ করেছেন (সি ++ পদ্ধতিতে স্ট্রিংগুলির সাথে কাজ করুন) বা নিক্ষেপের ঠিক আগে - std::bad_allocব্যতিক্রম আপনি যা চান তা আগে ফেলে দেওয়া যেতে পারে।

সুতরাং, স্ট্যাকের জন্য বরাদ্দকৃত একটি বাফার (ম্যাক্সিমমের উত্তরের মতো) একটি নিরাপদ উপায়।

এটি http://www.boost.org/commune/error_handling.html এ খুব ভালভাবে ব্যাখ্যা করা হয়েছে

সুতরাং, সুন্দর উপায়টি ব্যতিক্রমগুলির একটি নির্দিষ্ট ধরণের হবে এবং বিন্যাসিত স্ট্রিং (কমপক্ষে নিক্ষেপ করার সময়) রচনা করা এড়ানো হবে।


0

আমার কাস্টম ব্যতিক্রমগুলির জন্য কাস্টম ত্রুটি বার্তাগুলি তৈরি করা কুৎসিত কোড তৈরি করে, একই ধরণের সমস্যায় পড়ে। এটি আমার সমাধান ছিল:

class MyRunTimeException: public std::runtime_error
{
public:
      MyRunTimeException(const std::string &filename):std::runtime_error(GetMessage(filename)) {}
private:
      static std::string GetMessage(const std::string &filename)
     {
           // Do your message formatting here. 
           // The benefit of returning std::string, is that the compiler will make sure the buffer is good for the length of the constructor call
           // You can use a local std::ostringstream here, and return os.str()
           // Without worrying that the memory is out of scope. It'll get copied
           // You also can create multiple GetMessage functions that take all sorts of objects and add multiple constructors for your exception
     }
}

এটি বার্তা তৈরির জন্য যুক্তি পৃথক করে। আমি প্রথমে কী () কে ওভাররাইড করার কথা ভেবেছিলাম তবে তারপরে আপনাকে আপনার বার্তাটি কোথাও ক্যাপচার করতে হবে। std :: রানটাইম_অররর ইতিমধ্যে একটি অভ্যন্তরীণ বাফার রয়েছে।


0

হয়তো এই?

throw std::runtime_error(
    (std::ostringstream()
        << "Could not load config file '"
        << configfile
        << "'"
    ).str()
);

এটি অস্থায়ী অস্ট্রিং স্ট্রিম তৈরি করে, << অপারেটরগুলিকে প্রয়োজনীয় হিসাবে কল করে এবং তারপরে আপনি এটি বৃত্তাকার বন্ধনীগুলিতে আবদ্ধ করুন এবং অস্থায়ী স্টাড :: স্ট্রিংটি কনস্ট্রাক্টারে পাস করার জন্য মূল্যায়িত ফলাফলের (। একটি অস্ট্রিংস্ট্রিম) ফাংশনটিকে কল করুন রানটাইম_অররারের।

দ্রষ্টব্য: অস্ট্রিংস্ট্রিম এবং স্ট্রিংগুলি r- মান অস্থায়ী এবং তাই এই লাইনটি শেষ হওয়ার পরে সুযোগের বাইরে চলে যায়। আপনার ব্যতিক্রম অবজেক্টের কনস্ট্রাক্টর অবশ্যই কপি বা (আরও ভাল) মুভি শব্দার্থক ব্যবহার করে ইনপুট স্ট্রিংটি নিতে হবে।

অতিরিক্ত: আমি অগত্যা এই পদ্ধতিকে "সেরা অনুশীলন" হিসাবে বিবেচনা করি না, তবে এটি কাজ করে এবং একটি চিমটি ব্যবহার করা যেতে পারে। সবচেয়ে বড় সমস্যাগুলির মধ্যে একটি হ'ল এই পদ্ধতিটির জন্য হিপ বরাদ্দ প্রয়োজন এবং তাই অপারেটর << ফেলতে পারে। আপনি সম্ভবত যে ঘটতে চান না; তবে, যদি আপনি সেই অবস্থায় প্রবেশ করেন তবে আপনার সম্ভবত আরও সমস্যা নিয়ে চিন্তার উপায় রয়েছে!

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.